若依cloud -【 22 ~ 25 】

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,
}

<think>嗯,现在用户遇到了Debian系统中ufw命令未找到的问题,得好好帮ta解决。用户之前提到参考站内引用,虽然引用内容有些相关,但主要还是关于ufw和Docker的问题,而用户当前的问题是无法找到ufw命令。需要思考Debian系统中ufw的安装情况。Debian不像Ubuntu那样默认预装ufw,所以可能根本没安装。应该首先建议用户安装ufw包。另外用户可能是最小化安装系统,缺少必要组件。用户可能是Linux新手,遇到命令不存在时可能会困惑。在回答时要给出明确的安装步骤,包括aptupdate和aptinstallufw的具体命令。考虑到用户权限问题,需要提醒使用sudo。安装后的服务状态检查也很重要,因为ufw安装后默认是非激活状态。还需要考虑环境变量问题,虽然不太常见,但万一用户安装在非标准路径,可以补充说明echo$PATH的检查方法。最后引导用户进行防火墙基本操作测试,确保安装成功。用户引用中提到docker和ufw的兼容性问题,虽然这不是当前问题,但可以提及后续可能遇到的相关问题作为延伸。回答要结构化,先解决安装问题,再补充相关提示。</think> 好的,解决 Debian 系统中 `ufw` 命令提示 "未找到" 的问题,核心原因是 **`ufw` 工具在 Debian 上默认没有安装**。以下是详细的解决方案步骤: ### 🔧 解决方案步骤 1. **📥 更新软件包列表** (建议操作): 首先获取最新的软件包信息,确保安装命令使用的是最新的源数据。 ```bash sudo apt update ``` 2. **🛠️ 安装 `ufw` 软件包**: 这是解决问题的核心步骤。使用 Debian 的包管理工具 `apt` 进行安装。 ```bash sudo apt install ufw -y ``` * `sudo`: 以管理员权限执行安装命令(需要用户具有sudo权限)。 * `apt install ufw`: 安装名为 `ufw` 的软件包。 * `-y`: 对安装过程中的确认提示自动回答 "yes" (可选,但推荐使用)。 3. **✅ 验证安装**: 安装完成后,输入以下命令检查 `ufw` 是否已成功安装并能正常运行: ```bash ufw --version ``` 或者查看其状态(安装后默认处于非激活状态): ```bash sudo ufw status ``` 命令应该不再提示 "未找到",而是会显示版本信息或状态(通常是 `inactive`)和当前规则。 4. **(可选) 启用并测试基本功能**: * 启用 `ufw`(此命令会启用防火墙并设置默认阻止所有传入连接,允许所有传出连接): ```bash sudo ufw enable ``` ⚠️ **注意**: 在远程服务器上执行 `ufw enable` 前,务必确认已配置好允许你当前连接的端口规则(通常是 SSH 端口 22),否则可能会导致你被踢出服务器! * 允许 SSH (推荐在启用前设置): ```bash sudo ufw allow ssh # 或明确指定端口 sudo ufw allow 22/tcp ``` * 查看更详细的状态: ```bash sudo ufw status verbose ``` ### 💡 重要说明 * **默认不安装**: `ufw` 在 Debian 默认安装中通常不会包含,与 Ubuntu 不同。遇到 `ufw: command not found` 是正常现象,需要手动安装它来管理防火墙[^1]。 * **权限要求**: 安装、启用和管理 `ufw` 都需要管理员权限,因此务必使用 `sudo` 命令。 * **防火墙策略**: 安装并启用 `ufw` 后,它就是你的系统防火墙。在配置规则时请务必小心,确保不会意外阻止必要的服务(特别是远程连接使用的 SSH)。如果管理的是远程服务器,启用防火墙前配置好 SSH 规则至关重要。 * **引用关联**: 你提到的引用 [2] 虽然主要是关于 Docker 和 ufw 路由冲突的问题(这在使用 Docker 时非常重要),但它也间接证明了 `ufw` 在 Linux 防火墙管理中的核心作用[^2]。只有首先解决了 `ufw` 本身的安装和启用问题,才能继续解决如 Docker 绕过 ufw 这样的进阶问题。 ### 🧪 测试安装后 安装完成后,再次尝试运行你原本想执行的 `ufw` 命令(如 `sudo ufw status`),确认问题已解决。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值