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)