若依cloud -【 22 ~ 25 】

news2024/12/23 6:23:22

22 认证中心介绍

1 概述

  1. 用户身份认证的过程
  2. ruoyi-cloud认证中心的实现没有依赖任何插件,相对简单,一看就懂
  3. 从架构图的角度看认证中心:

    ruoyi-cloud

    1. 登录请求,进到网关
    2. 网关直接调用认证中心。查看ruoyi-gateway-dev.yml:
      
      # 结论:认证服务应用的话,是没有验证码的处理的。
      # 关于验证码的处理,是在网关这边就处理完了。
      spring:
        redis:
          host: localhost
          port: 6379
          password:
        cloud:
          gateway:
            discovery:
              locator:
                lowerCaseServiceId: true
                enabled: true
            routes:
              - id: ruoyi-auth
                uri: lb://ruoyi-auth
                # 第二步:调用后端的认证中心接口  
                predicates:
                  - Path=/auth/**
                filters:
                  # 第一步:校验验证码
                  - CacheRequestFilter
                  - ValidateCodeFilter
                  - StripPrefix=1

2 内容

(1)TokenController.java:登录接口、登出接口、刷新令牌接口、用户注册接口。

(2)LoginBody:用户登录对象,包含username、password这2个属性。

(3)SysLoginService:登录校验方法。服务之间的相互调用,比如在认证模块认证完成后,还要记录日志。

(4)RuoYiAuthApplication:启动类

(5)bootstrap.yml:配置文件 

# Tomcat
server: 
  port: 9200

# Spring
spring: 
  application:
    # 应用名称
    name: ruoyi-auth
  profiles:
    # 环境配置
    active: dev
  cloud:
    nacos:
      discovery:
        # 服务注册地址
        server-addr: 127.0.0.1:8848
      config:
        # 配置中心地址
        server-addr: 127.0.0.1:8848
        # 配置文件格式
        file-extension: yml
        # 共享配置
        shared-configs:
          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

3 如何使用认证中心去认证?

(1)添加依赖

<!-- ruoyi common security-->
<dependency>
	<groupId>com.ruoyi</groupId>
	<artifactId>ruoyi-common-security</artifactId>
</dependency>

(2)认证启动类

/**
 * 认证授权中心
 */
/**
 * 自定义的ruoyi-security模块提供的注解,
 * 会去加载Feign的注解,
 * 
 */
@EnableRyFeignClients
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class RuoYiAuthApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(RuoYiAuthApplication.class, args);
        System.out.println("(♥◠‿◠)ノ゙  认证授权中心启动成功   ლ(´ڡ`ლ)゙  \n" +
                " .-------.       ____     __        \n" +
                " |  _ _   \\      \\   \\   /  /    \n" +
                " | ( ' )  |       \\  _. /  '       \n" +
                " |(_ o _) /        _( )_ .'         \n" +
                " | (_,_).' __  ___(_ o _)'          \n" +
                " |  |\\ \\  |  ||   |(_,_)'         \n" +
                " |  | \\ `'   /|   `-'  /           \n" +
                " |  |  \\    /  \\      /           \n" +
                " ''-'   `'-'    `-..-'              ");
    }
}

 提示:

       目前已经存在ruoyi-auth认证授权中心,用于登录认证,系统退出,刷新令牌。

(3)ruoyi-security#EnableRyFeignClients:自定义的feign注解

/**
 * 自定义feign注解
 * 添加basePackages路径
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableRyFeignClients
{
    String[] value() default {};

    /**
     * 扫描"com.ruoyi"包及其子孙所下面的所有feign注解,
     * 给它注入进来,不然我们就会实现不了服务之间的相互调用了。
     */
    String[] basePackages() default { "com.ruoyi" };

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

23 登录认证实现

1、ruoyi-auth#TokenController#login:登录接口

    @PostMapping("login")
    // LoginBody用户对象,包含了username、password
    public R<?> login(@RequestBody LoginBody form)
    {
        // 用户登录
        LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());
        // 获取登录token
        return R.ok(tokenService.createToken(userInfo));
    }

2、ruoyi-auth#SysLoginService#login:登录业务类

    /**
     * 登录
     */
    public LoginUser login(String username, String password)
    {
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password))
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
            throw new ServiceException("用户/密码必须填写");
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
            throw new ServiceException("用户密码不在指定范围");
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
            throw new ServiceException("用户名不在指定范围");
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
        }
        /**
         * openfeign(RPC)实现服务之间的相互调用,查询数据库获取LoginUser对象信息。
         * LoginUser包含:用户令牌、用户的角色集合、用户的权限集合。
          */
        R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);

        if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
            throw new ServiceException("登录用户:" + username + " 不存在");
        }

        if (R.FAIL == userResult.getCode())
        {
            throw new ServiceException(userResult.getMsg());
        }
        
        LoginUser userInfo = userResult.getData();
        SysUser user = userResult.getData().getSysUser();
        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        }
        if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
        }
        /**
         * 密码校验
         */
        passwordService.validate(user, password);
        /**
         * 记录登录日志
         */
        recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
        return userInfo;
    }

3、ruoyi-system#SysUserController#info:获取LoginUser对象的信息

    /**
     * 获取当前用户信息
     */
    @InnerAuth
    @GetMapping("/info/{username}")
    public R<LoginUser> info(@PathVariable("username") String username)
    {
        SysUser sysUser = userService.selectUserByUserName(username);
        if (StringUtils.isNull(sysUser))
        {
            return R.fail("用户名或密码错误");
        }
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(sysUser);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(sysUser);
        LoginUser sysUserVo = new LoginUser();
        sysUserVo.setSysUser(sysUser);
        sysUserVo.setRoles(roles);
        sysUserVo.setPermissions(permissions);
        return R.ok(sysUserVo);
    }

4、ruoyi-common-security#TokenService#createToken:生成token

    /**
     * 创建令牌
     */
    public Map<String, Object> createToken(LoginUser loginUser)
    {
        String token = IdUtils.fastUUID();
        Long userId = loginUser.getSysUser().getUserId();
        String userName = loginUser.getSysUser().getUserName();
        loginUser.setToken(token);
        loginUser.setUserid(userId);
        loginUser.setUsername(userName);
        loginUser.setIpaddr(IpUtils.getIpAddr());
        /**
         * redis缓存中数据要刷新一下
         */
        refreshToken(loginUser);

        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(SecurityConstants.USER_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);

        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<String, Object>();
        rspMap.put("access_token", JwtUtils.createToken(claimsMap));
        rspMap.put("expires_in", expireTime);
        return rspMap;
    }

5、ruoyi-common-security#TokenService#verifyToken:验证令牌有效期,相差不足120分钟,自动刷新缓存

    /**
     * 验证令牌有效期,相差不足120分钟,自动刷新缓存
     *
     * @param loginUser
     */
    public void verifyToken(LoginUser loginUser)
    {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }

6、ruoyi-common-security#TokenService#refreshToken:刷新令牌有效期

    /**
     * 刷新令牌有效期
     *
     * @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());
        /**
         * 往redis中保存数据
         */
        redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

7、ruoyi-common-core#CacheConstants#LOGIN_TOKEN_KEY:权限缓存前缀

    /**
     * 权限缓存前缀
     */
    public final static String LOGIN_TOKEN_KEY = "login_tokens:";

8、测试

用户登录接口地址 http://localhost:9200/login

请求头Content-Type - application/json,请求方式Post

{
    "username": "admin",
    "password": "admin123"
}

响应结果

{
    "code": 200,
    "data": {
        "access_token": "f840488c-68a9-4272-acc9-c34d3b66a943",
        "expires_in": 43200
    }
}

通过用户验证登录后获取access_token,通过网关访问其他应用数据时必须携带此参数值。

24 刷新令牌实现

1、ruoyi-auth#TokenController#refresh:刷新令牌接口

    @PostMapping("refresh")
    /**
     * request必须包含token
     */
    public R<?> refresh(HttpServletRequest request)
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser))
        {
            // 刷新令牌有效期
            tokenService.refreshToken(loginUser);
            return R.ok();
        }
        return R.ok();
    }

2、ruoyi-common-security#TokenService#getLoginUser:获取登录用户令牌

/**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServletRequest request)
    {
        // 获取请求携带的令牌
        String token = SecurityUtils.getToken(request);
        return getLoginUser(token);
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(String token)
    {
        LoginUser user = null;
        try
        {
            if (StringUtils.isNotEmpty(token))
            {
                String userkey = JwtUtils.getUserKey(token);
                /**
                 * 从缓存中获取LoginUser
                 */
                user = redisService.getCacheObject(getTokenKey(userkey));
                return user;
            }
        }
        catch (Exception e)
        {
            log.error("获取用户信息异常'{}'", e.getMessage());
        }
        return user;
    }

3、ruoyi-common-security#TokenService#refreshToken:刷新令牌有效期

    /**
     * 刷新令牌有效期
     *
     * @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());
        /**
         * 往redis中保存数据
         */
        redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

4、测试postman

顾名思义,就是对系统操作用户的进行缓存刷新,防止过期。

TokenController控制器refresh方法会在用户调用时更新令牌有效期。

刷新令牌接口地址 http://localhost:9200/refresh

请求头Authorization - f840488c-68a9-4272-acc9-c34d3b66a943,请求方式Post

响应结果

{
    "code": 200,
}

刷新后有效期为默720(分钟)。

25 系统退出实现    

顾名思义,就是对系统登用户的退出过程。

TokenController控制器logout方法会在用户退出时删除缓存信息同时保存用户退出日志。源码ruoyi-auth#TokenController.logout:

    @DeleteMapping("logout")
    public R<?> logout(HttpServletRequest request)
    {
        String token = SecurityUtils.getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
            String username = JwtUtils.getUserName(token);
            // 删除用户缓存记录
            AuthUtil.logoutByToken(token);
            // 记录用户退出日志
            sysLoginService.logout(username);
        }
        return R.ok();
    }

系统退出接口地址 http://localhost:9200/logout

请求头Authorization - f840488c-68a9-4272-acc9-c34d3b66a943,请求方式Delete

{
    "username": "admin",
    "password": "admin123"
}

响应结果

{
    "code": 200,
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【历史上的今天】8 月 22 日:改变世界的程序员们;网络直播的鼻祖;何小鹏离开阿里巴巴

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天 2023 年 8 月 22 日。历史上的今天&#xff0c;Masatoshi Shima 出生&#xff0c;他和 英特尔&#xff08;Intel&#xff09; 合作设计的芯片让第一个微处理器 Intel 400…

【SpringSecurity】二、密码处理与获取当前登录用户

文章目录 一、密码处理1、加密方案2、BCryptPasswordEncoder类初体验3、使用加密码加密 二、获取当前登录用户1、方式一&#xff1a;通过安全上下文的静态调用2、方式二&#xff1a;做为Controller中方法的参数3、方式三&#xff1a;从HTTPServletRequest中获取4、方式四&#…

无涯教程-PHP Installation on Windows NT/2000/XP with IIS函数

在Windows Server上运行IIS的PHP的安装比在Unix上简单得多,因为它涉及的是预编译的二进制文件而不是源代码。 如果您打算在Windows上安装PHP,那么这是先决条件列表- 运行中的PHP支持的Web服务器。一个正确安装的PHP支持的数据库,如MySQL或Oracle等。(如果您打算使用的话) PHP…

smiley-http-proxy-servlet 实现springboot 接口反向代理,站点代理,项目鉴权,安全的引入第三方项目服务

背景&#xff1a; 项目初期 和硬件集成&#xff0c;实现了些功能服务&#xff0c;由于是局域网环境&#xff0c;安全问题当时都可以最小化无视。随着对接的服务越来越多&#xff0c;部分功能上云&#xff0c;此时就需要有一种手段可以控制到其他项目/接口的访问权限。 无疑 反向…

硬件解码example

使用方法&#xff1a; ./hw_decode vaapi juren-30s.mp4 juren-30s.mp4 验证播放&#xff1a; ffplay -video_size 1920x1080 -pixel_format yuv420p juren-30s.yuv av_hwdevice_iterate_types(type)如果你填入的参数不对&#xff0c;那么这函数这个函数的作用相当于以下命令&…

飞天使-k8s基础组件分析-pod

文章目录 pod介绍pod 生命周期init 容器容器handlerpod中容器共享进程空间sidecar 容器共享 参考链接 pod介绍 最小的容器单元 为啥需要pod? 答: 多个进程丢一个容器里&#xff0c;会因为容器里个别进程出问题而出现蝴蝶效应&#xff0c;pod 是更高级的处理方式pod 如何共享相…

简述docker映射(Mapping)和挂载(Mounting)

映射的概念&#xff1a; 将容器内的端口映射到主机的端口上&#xff0c;这样就可以通过主机的网络接口与容器内部进行通信。主机上对应端口的请求会被转发到容器内部&#xff0c;从而实现对容器内部程序的通信访问&#xff08;注意&#xff01;这里提到的容器内部的端口并不一定…

网络安全(黑客)了解学习路线

谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一般分为三大圈&#xff1a;娱乐圈 技术圈 职业圈。 娱乐圈&#xff1a;主要是初中生和高中生较多&#xff0c;玩网恋…

EasyImage简单图床 - 快速搭建私人图床云盘同时远程访问【无公网IP内网穿透】

憧憬blog主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是移动开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索iOS开发的无限可能&#xff01; &#x1f525;我们与您分享最新的技术洞察和实战经验&#xff0…

阿里云CDN加速器基本概念与购买开通

文章目录 1.CDN加速器的基本概念1.1.CDN加速器基本介绍1.2.网站引入CDN加速器的架构图1.3.CDN加速器的工作原理1.4.引入CDN后域名解析变成了CNAME&#xff1f; 2.开通阿里云CDN加速服务 1.CDN加速器的基本概念 CDN加速器官方文档&#xff1a;https://help.aliyun.com/product/…

USB音频芯片SSS1700 鑫创优势替代CM6533参考设计|SSS1700规格24bit 96KHZ |替换CM6533方案

Cmedia CM6533是一款US B音频编解码器&#xff0c;内部嵌入8051内核&#xff0c;适用于耳麦&#xff0c;移动娱乐设备直插移动音箱&#xff08;docking&#xff09;&#xff0c;US B音箱&#xff0c;US B麦克风等应用。通过内部8051可以研发出各种应用&#xff0c;例如微软语音…

vue3 pdf、word等文件下载

效果&#xff1a; <div class"byLawBox"><div class"titleBox">规章制度公示</div><div class"contentBox"><TableList:loading"byLawloading"ref"byLawtablistRef":hasImport"false"…

Unity - 特殊文件夹

地址记录&#xff1a;https://www.cnblogs.com/zouqiang/p/6841399.html Assets Assets文件夹是unity项目中放置游戏资源的主文件夹。 该文件夹中的内容将直接反应在编辑器的Project视口中。许多系统API基于该文件夹路径。 Resources Unity允许你按需动态加载游戏资源到场景中…

ubuntu 对多CPU统一设置高性能模式

一、问题描述 之前在网上找到的CPU设置高性能模式&#xff0c;只能设置CPU0单个CPU&#xff0c;下述是对多核CPU统一设置工作模式。 二、软件安装与设置 执行下述命令sudo apt-get install indicator-cpufreq,然后重启电脑。此时&#xff0c;界面右上角会出现如下图标&#xf…

爬虫异常捕获与处理方法详解

Hey&#xff01;作为一名专业的爬虫代理供应商&#xff0c;我今天要和大家分享一些关于爬虫异常捕获与处理的方法。在进行爬虫操作时&#xff0c;我们经常会遇到各种异常情况&#xff0c;例如网络连接错误、请求超时、数据解析错误等等。这些异常情况可能会导致程序崩溃或数据丢…

威班8.19PMP考试爱心送考再出发,能量补给站为学员提供考试保障

8月19日&#xff0c;2023年的第三场PMP考试准时开考。 对于准备了个把月的学员们来说&#xff0c;这一时刻无比重要&#xff0c;为了给威班的PMP学员们考试加油打气&#xff0c;威班特地组织了送考活动&#xff0c;在考场外提前布置应援点&#xff0c;给前去参加考试的学员们带…

5.1 汇编语言:汇编语言概述

汇编语言是一种面向机器的低级语言&#xff0c;用于编写计算机程序。汇编语言与计算机机器语言非常接近&#xff0c;汇编语言程序可以使用符号、助记符等来代替机器语言的二进制码&#xff0c;但最终会被汇编器编译成计算机可执行的机器码。 相较于高级语言&#xff08;如C、P…

紧跟老板思维,这款数据可视化工具神了

在今时今日&#xff0c;依靠大数据可视化分析工具做出一张形象直观、符合企业审美的数据可视化分析报表都不是什么难事&#xff0c;难就难在于做出一张能够紧随老板思维变化展开可视化分析的数据可视化分析报表。接下来要介绍的这款数据可视化工具就是这么一款神奇的BI工具&…

整理文件轻松搞定,教你如何将文件夹里的文件进行类型归类保存

大家好&#xff01;相信大家在日常工作和学习中都会面临着大量的文件管理问题&#xff0c;如何高效地整理和保存文件成为了一大难题。幸运的是&#xff0c;不仅可以帮助你高效整理文件&#xff0c;还能实现文件的类型归类保存。 首先&#xff0c;我们要进入文件批量查询高手主…

七夕给TA满分宠爱!浪漫攻略为约会加分

浪漫的七夕将至&#xff0c;无论是异地恋人还是约会情侣&#xff0c;怎么能缺少节日仪式感~精心策划的约会计划&#xff0c;让浪漫“超级加倍”。 美好的二人世界&#xff0c;共度甜蜜时光&#xff0c;当然需要提前做好攻略&#xff0c;风和日丽的好天气能为约会加分不少。在规…