2.若依中的认证中心

求知律己 / 2023-08-23 / 原文

1.前言

   在本节中主要是介绍若依微服务版本中的认证功能以及流程实现,认证功能主要包含注册、登录认证,用户注销,刷新token等。

 2.项目实现

 2.1 导入依赖

  在该依赖中包含nacos注册发现、配置、sentinel、web、Actuator、ruoyi-common-security等依赖包,其中ruoyi-common-security需要我们导入依赖并搭建项目作为ruoyi-auth模块的子模块

<dependencies>
        
        <!-- SpringCloud Alibaba Nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        
        <!-- SpringCloud Alibaba Nacos Config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        
        <!-- SpringCloud Alibaba Sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        
        <!-- SpringBoot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- SpringBoot Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <!-- RuoYi Common Security-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-security</artifactId>
        </dependency>
        
    </dependencies>
ruoyi-auth依赖

  在ruoyi-common-security模块中包含webmvc、ruoyi-system、ruoyi-common-redis等依赖

<dependencies>

        <!-- Spring Web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>

        <!-- RuoYi Api System -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-system</artifactId>
        </dependency>

        <!-- RuoYi Common Redis-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>

    </dependencies>
ruoyi-common-security依赖

  在ruoyi-common-redis模块中包含redis、ruoyi-common-core等依赖

<dependencies>
        
        <!-- SpringBoot Boot Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- RuoYi Common Core-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        
    </dependencies>
ruoyi-common-redis依赖

  在ruoyi-common-core模块中包含OpenFeign、loadBalancer、SpringContextSupport、web、Transmittable ThreadLocal、PageHelper、Hibernate Validator、Jackson、FastJSON、Jwt、jaxb、lang、IO、excel、servlet、swagger等依赖。

<dependencies>

        <!-- SpringCloud Openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        
        <!-- SpringCloud Loadbalancer -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

        <!-- Spring Context Support -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <!-- Spring Web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>

        <!-- Transmittable ThreadLocal -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
        </dependency>

        <!-- Pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>

        <!-- Hibernate Validator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <!-- Alibaba Fastjson -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>

        <!-- Jwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>

        <!-- Jaxb -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
        </dependency>

        <!-- Apache Lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!-- Commons Io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>

        <!-- excel工具 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>

        <!-- Java Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>

        <!-- Swagger -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
        </dependency>

    </dependencies>
ruoyi-common-core

2.2 编写bootstrap.yml文件

   在该配置文件中包该模块的服务端口、应用名称、环境、nacos注册地址、配置中心地址、配置格式以及共享配置文件。

# 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
        # 共享配置,即将application.dev.yml作为共享配置
        shared-configs:
          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
bootstrap.yml文件

2.3 form类(注册和登录实体类)

  在用户登录和注册实体类中,注册实体类继承了登录实体类,他们都只有username和password字段以及对应get和set方法

package com.ruoyi.auth.form;

/**
 * 用户登录对象
 * 
 * @author ruoyi
 */
public class LoginBody
{
    /**
     * 用户名
     */
    private String username;

    /**
     * 用户密码
     */
    private String password;

    public String getUsername()
    {
        return username;
    }

    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }
}
用户登录实体类
package com.ruoyi.auth.form;

/**
 * 用户注册对象
 * 
 * @author ruoyi
 */
public class RegisterBody extends LoginBody
{

}
用户注册实体类

2.4 service包(用户登录逻辑类、登录密码逻辑类、记录日志类)

 2.4.1 SysLoginService

  该服务类是通过@Component注解注入到Spring容器中,该类包含用户注册、登录、注销流程。

  注册流程主要是检验注册用户/密码是否填写、账户长度、密码长度等问题,接着设置用户信息,并通过远程调用RemoteUserService实现用户注册,并将注册信息记录同步到数据库中。

  登录流程主要是检验用户/密码是否填写、用户名和密码是否在指定范围、IP是否在黑名单、登录用户是否存在,是否删除、禁用。然后根据用户信息和密码通过调用passwordService验证密码,最后通过SysRecordLogService实例同步登录日志记录到数据库。

  注销流程通过SysRecordLogService实例同步注销日志记录到数据库

  1 package com.ruoyi.auth.service;
  2 
  3 import org.springframework.beans.factory.annotation.Autowired;
  4 import org.springframework.stereotype.Component;
  5 import com.ruoyi.common.core.constant.CacheConstants;
  6 import com.ruoyi.common.core.constant.Constants;
  7 import com.ruoyi.common.core.constant.SecurityConstants;
  8 import com.ruoyi.common.core.constant.UserConstants;
  9 import com.ruoyi.common.core.domain.R;
 10 import com.ruoyi.common.core.enums.UserStatus;
 11 import com.ruoyi.common.core.exception.ServiceException;
 12 import com.ruoyi.common.core.text.Convert;
 13 import com.ruoyi.common.core.utils.StringUtils;
 14 import com.ruoyi.common.core.utils.ip.IpUtils;
 15 import com.ruoyi.common.redis.service.RedisService;
 16 import com.ruoyi.common.security.utils.SecurityUtils;
 17 import com.ruoyi.system.api.RemoteUserService;
 18 import com.ruoyi.system.api.domain.SysUser;
 19 import com.ruoyi.system.api.model.LoginUser;
 20 
 21 /**
 22  * 登录校验方法
 23  * 
 24  * @author ruoyi
 25  */
 26 @Component
 27 public class SysLoginService
 28 {
 29     @Autowired
 30     private RemoteUserService remoteUserService;
 31 
 32     @Autowired
 33     private SysPasswordService passwordService;
 34 
 35     @Autowired
 36     private SysRecordLogService recordLogService;
 37 
 38     @Autowired
 39     private RedisService redisService;
 40 
 41     /**
 42      * 登录
 43      */
 44     public LoginUser login(String username, String password)
 45     {
 46         // 用户名或密码为空 错误
 47         if (StringUtils.isAnyBlank(username, password))
 48         {
 49             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
 50             throw new ServiceException("用户/密码必须填写");
 51         }
 52         // 密码如果不在指定范围内 错误
 53         if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
 54                 || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
 55         {
 56             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
 57             throw new ServiceException("用户密码不在指定范围");
 58         }
 59         // 用户名不在指定范围内 错误
 60         if (username.length() < UserConstants.USERNAME_MIN_LENGTH
 61                 || username.length() > UserConstants.USERNAME_MAX_LENGTH)
 62         {
 63             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
 64             throw new ServiceException("用户名不在指定范围");
 65         }
 66         // IP黑名单校验
 67         String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
 68         if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
 69         {
 70             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单");
 71             throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
 72         }
 73         // 查询用户信息
 74         R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
 75 
 76         if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
 77         {
 78             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
 79             throw new ServiceException("登录用户:" + username + " 不存在");
 80         }
 81 
 82         if (R.FAIL == userResult.getCode())
 83         {
 84             throw new ServiceException(userResult.getMsg());
 85         }
 86         
 87         LoginUser userInfo = userResult.getData();
 88         SysUser user = userResult.getData().getSysUser();
 89         if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
 90         {
 91             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
 92             throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
 93         }
 94         if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
 95         {
 96             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
 97             throw new ServiceException("对不起,您的账号:" + username + " 已停用");
 98         }
 99         passwordService.validate(user, password);
100         recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
101         return userInfo;
102     }
103 
104     public void logout(String loginName)
105     {
106         recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
107     }
108 
109     /**
110      * 注册
111      */
112     public void register(String username, String password)
113     {
114         // 用户名或密码为空 错误
115         if (StringUtils.isAnyBlank(username, password))
116         {
117             throw new ServiceException("用户/密码必须填写");
118         }
119         if (username.length() < UserConstants.USERNAME_MIN_LENGTH
120                 || username.length() > UserConstants.USERNAME_MAX_LENGTH)
121         {
122             throw new ServiceException("账户长度必须在2到20个字符之间");
123         }
124         if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
125                 || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
126         {
127             throw new ServiceException("密码长度必须在5到20个字符之间");
128         }
129 
130         // 注册用户信息
131         SysUser sysUser = new SysUser();
132         sysUser.setUserName(username);
133         sysUser.setNickName(username);
134         sysUser.setPassword(SecurityUtils.encryptPassword(password));
135         R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);
136 
137         if (R.FAIL == registerResult.getCode())
138         {
139             throw new ServiceException(registerResult.getMsg());
140         }
141         recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功");
142     }
143 }
SysLoginService

2.4.2 SysPasswordService

 

2.4.3 SysRecordLogService

项目链接

RuoYi-Cloud: 🎉 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统,同时提供了 Vue3 的版本 (gitee.com)