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 SysRecordLogService

  该服务类通过@Component注解注入到Spring容器中,只用于记录用户登录信息。通过创建登录信息对象记载登录用户的用户名、IP、消息,之后通过日志服务调用对象remoteLogService完成用户登录信息日志记录。

package com.ruoyi.auth.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.system.api.domain.SysLogininfor;

/**
 * 记录日志方法
 * 
 * @author ruoyi
 */
@Component
public class SysRecordLogService
{
    @Autowired
    private RemoteLogService remoteLogService;

    /**
     * 记录登录信息
     * 
     * @param username 用户名
     * @param status 状态
     * @param message 消息内容
     * @return
     */
    public void recordLogininfor(String username, String status, String message)
    {
        SysLogininfor logininfor = new SysLogininfor();
        logininfor.setUserName(username);
        logininfor.setIpaddr(IpUtils.getIpAddr());

        logininfor.setMsg(message);
        // 日志状态
        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
        {
            logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
        }
        else if (Constants.LOGIN_FAIL.equals(status))
        {
            logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
        }
        remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
    }
}

SysRecordLogService

2.4.2 SysPasswordService

   该服务类通过@Component注解注入到Spring容器中,用于验证用户密码登录。主要通过Redis根据用户名获取Redis缓存的key,通过该key将输入密码错误次数作为值,并设置过期时间缓存在Redis中。验证完成后,清除Redis中的缓存。

1 package com.ruoyi.auth.service;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.stereotype.Component;
 6 import com.ruoyi.common.core.constant.CacheConstants;
 7 import com.ruoyi.common.core.constant.Constants;
 8 import com.ruoyi.common.core.exception.ServiceException;
 9 import com.ruoyi.common.redis.service.RedisService;
10 import com.ruoyi.common.security.utils.SecurityUtils;
11 import com.ruoyi.system.api.domain.SysUser;
12 
13 /**
14  * 登录密码方法
15  * 
16  * @author ruoyi
17  */
18 @Component
19 public class SysPasswordService
20 {
21     @Autowired
22     private RedisService redisService;
23 
24     private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT;
25 
26     private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME;
27 
28     @Autowired
29     private SysRecordLogService recordLogService;
30 
31     /**
32      * 登录账户密码错误次数缓存键名
33      * 
34      * @param username 用户名
35      * @return 缓存键key
36      */
37     private String getCacheKey(String username)
38     {
39         return CacheConstants.PWD_ERR_CNT_KEY + username;
40     }
41 
42     public void validate(SysUser user, String password)
43     {
44         String username = user.getUserName();
45 
46         Integer retryCount = redisService.getCacheObject(getCacheKey(username));
47 
48         if (retryCount == null)
49         {
50             retryCount = 0;
51         }
52 
53         if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
54         {
55             String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime);
56             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL,errMsg);
57             throw new ServiceException(errMsg);
58         }
59 
60         if (!matches(user, password))
61         {
62             retryCount = retryCount + 1;
63             recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount));
64             redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
65             throw new ServiceException("用户不存在/密码错误");
66         }
67         else
68         {
69             clearLoginRecordCache(username);
70         }
71     }
72 
73     public boolean matches(SysUser user, String rawPassword)
74     {
75         return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
76     }
77 
78     public void clearLoginRecordCache(String loginName)
79     {
80         if (redisService.hasKey(getCacheKey(loginName)))
81         {
82             redisService.deleteObject(getCacheKey(loginName));
83         }
84     }
85 }

SysPasswordService

2.4.3 SysLoginService

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

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

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

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

SysLoginService

2.5 TokenController

  该类主要根据映射路径进行业务处理并返回业务处理结果。其中包含用户注册、登录、刷新token、注销等操作。

1 package com.ku.auth.controller;
 2 
 3 import com.ku.auth.form.LoginBody;
 4 import com.ku.auth.form.RegisterBody;
 5 import com.ku.auth.service.SysLoginService;
 6 import com.ku.common.core.domain.R;
 7 import com.ku.common.core.utils.JwtUtils;
 8 import com.ku.common.core.utils.StringUtils;
 9 import com.ku.common.security.auth.AuthUtil;
10 import com.ku.common.security.service.TokenService;
11 import com.ku.common.security.utils.SecurityUtils;
12 import com.ku.system.api.model.LoginUser;
13 import org.springframework.beans.factory.annotation.Autowired;
14 import org.springframework.web.bind.annotation.DeleteMapping;
15 import org.springframework.web.bind.annotation.PostMapping;
16 import org.springframework.web.bind.annotation.RequestBody;
17 import org.springframework.web.bind.annotation.RestController;
18 
19 import javax.servlet.http.HttpServletRequest;
20 
21 /**
22  * token控制
23  */
24 @RestController
25 public class TokenController {
26     @Autowired(required = false)
27     private TokenService tokenService;
28 
29     @Autowired(required = false)
30     private SysLoginService sysLoginService;
31 
32     @PostMapping("login")
33     public R<?> login(@RequestBody LoginBody form){
34         //用户登录
35         LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());
36         //获取用户token
37         return R.ok(tokenService.createToken(userInfo));
38     }
39 
40     @DeleteMapping("logout")
41     public R<?> logout(HttpServletRequest request){
42         String token = SecurityUtils.getToken(request);
43         if (StringUtils.isNotEmpty(token)){
44             String username = JwtUtils.getUserName(token);
45             //删除用户缓存记录
46             AuthUtil.logoutByToken(token);
47             sysLoginService.logout(username);
48         }
49         return R.ok();
50     }
51 
52     @PostMapping("refresh")
53     public R<?> refresh(HttpServletRequest request){
54         LoginUser loginUser = tokenService.getLoginUser(request);
55         if (StringUtils.isNotNull(loginUser)){
56             //刷新令牌有效期
57             tokenService.refreshToken(loginUser);
58             return R.ok();
59         }
60         return R.ok();
61     }
62 
63     @PostMapping("register")
64     public R<?> register(@RequestBody RegisterBody registerBody){
65         //用户注册
66         sysLoginService.register(registerBody.getUsername(), registerBody.getPassword());
67         return R.ok();
68     }
69 
70 }

TokenController

2.6 TokenService

  该类包含创建token,通过request获取token,通过token获取登录用户信息、根据登录用户设置用户信息、通过token删除用户、通过登录用户验证token,通过登录用户刷新令牌,通过token获取tokenKey

项目链接

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