Skip to content

RuoYi-Vue-Plus 项目中登录功能实现梳理

本文主要讲解 RuoYi-Vue-Plus 项目中登录功能的实现逻辑。

接收请求

首先在 AuthController 中的 login 方法中接收包含登录信息的请求体。

java
public class AuthController {

    @PostMapping("/login")
    public R<LoginVo> login(@RequestBody String body) {
        // 方法实现
    }
}

解析请求

使用 JsonUtils.parseObject 方法将请求体解析为 LoginBody 对象,并使用 ValidatorUtils.validate 方法进行验证。

java
// 解析请求
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);

// 验证
ValidatorUtils.validate(loginBody);

validate 方法中,使用了 Jakarta Bean Validation API 验证对象。大致逻辑描述如下:

  • 检索验证器实例:使用一个静态的验证器实例,即 SpringUtils.getBean(Validator.class) 从 Spring 应用程序上下文中检索。
  • 校验对象:接收一个对象和一个可选的验证组数组作为参数。调用 Validator 实例上的 validate 方法,传递对象和验证组。此方法返回一组 ConstraintViolation 对象,这些对象表示验证错误。
  • 检查验证错误:检查 ConstraintViolation 对象集是否为空,表明存在验证错误。
  • 抛出异常:如果存在验证错误,该方法抛出一个 ConstraintViolationException,传递一条消息和一组验证错误。
java
/**
 * Validator 校验框架工具
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ValidatorUtils {

    private static final Validator VALID = SpringUtils.getBean(Validator.class);

    /**
     * 对给定对象进行参数校验,并根据指定的校验组进行校验
     *
     * @param object 要进行校验的对象
     * @param groups 校验组
     * @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常
     */
    public static <T> void validate(T object, Class<?>... groups) {
        Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
        if (!validate.isEmpty()) {
            throw new ConstraintViolationException("参数校验异常", validate);
        }
    }

}
java
/**
 * 用户登录对象
 *
 * @author Lion Li
 */
@Data
public class LoginBody implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 客户端id
     */
    @NotBlank(message = "{auth.clientid.not.blank}")
    private String clientId;

    /**
     * 授权类型
     */
    @NotBlank(message = "{auth.grant.type.not.blank}")
    private String grantType;

    /**
     * 租户ID
     */
    private String tenantId;

    /**
     * 验证码
     */
    private String code;

    /**
     * 唯一标识
     */
    private String uuid;

}

验证客户端信息

根据 clientId 查询客户端信息,并验证客户端是否存在、授权类型是否匹配以及客户端状态是否正常。

java
// 授权类型和客户端id
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
SysClientVo client = clientService.queryByClientId(clientId);
// 查询不到 client 或 client 内不包含 grantType
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
    log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
    return R.fail(MessageUtils.message("auth.grant.type.error"));
} else if (!UserConstants.NORMAL.equals(client.getStatus())) {
    return R.fail(MessageUtils.message("auth.grant.type.blocked"));
}

校验租户

调用 loginService.checkTenant 方法校验租户信息。

java
// 校验租户
loginService.checkTenant(loginBody.getTenantId());

调用登录策略

根据授权类型调用相应的登录策略(如密码登录策略 PasswordAuthStrategy),并执行登录逻辑。

java
// 登录
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);

在登录策略的设计模式中,根据不同的授权类型(如密码登录、短信登录等)执行不同的登录逻辑。在项目中,登录策略通过接口 IAuthStrategy 和其实现类(如 PasswordAuthStrategySmsAuthStrategy)来实现。

首先在 IAuthStrategy 接口中包含一个静态方法 login,根据授权类型动态获取相应的策略实现。接着定义了登录方法 login,让实现类提供具体的登录逻辑。

java
/**
 * 授权策略
 *
 * @author Michelle.Chung
 */
public interface IAuthStrategy {

    String BASE_NAME = "AuthStrategy";

    /**
     * 登录
     *
     * @param body      登录对象
     * @param client    授权管理视图对象
     * @param grantType 授权类型
     * @return 登录验证信息
     */
    static LoginVo login(String body, SysClientVo client, String grantType) {
        // 授权类型和客户端id
        String beanName = grantType + BASE_NAME;
        if (!SpringUtils.containsBean(beanName)) {
            throw new ServiceException("授权类型不正确!");
        }
        IAuthStrategy instance = SpringUtils.getBean(beanName);
        return instance.login(body, client);
    }

    /**
     * 登录
     *
     * @param body   登录对象
     * @param client 授权管理视图对象
     * @return 登录验证信息
     */
    LoginVo login(String body, SysClientVo client);

}

然后是具体的登录实现类,通过 Spring 的依赖注入和动态获取 Bean 的方式,可以灵活地扩展和管理不同的登录策略。以密码登录实现类 PasswordAuthStrategy 为例。

  • 在该类中,实现了 IAuthStrategy 接口,提供密码登录的具体实现。
  • 使用 @Service("password" + IAuthStrategy.BASE_NAME) 注解,将该类注册为 Spring 容器中的一个服务,并指定其 Bean 名称为 passwordAuthStrategy
java
/**
 * 密码认证策略
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("password" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class PasswordAuthStrategy implements IAuthStrategy {

    @Override
    public LoginVo login(String body, SysClientVo client) {
        // 具体的实现逻辑
    }

}

生成登录用户信息

PasswordAuthStrategy 类的 login 方法中,解析登录请求体,校验验证码(如果启用)。

java
PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
ValidatorUtils.validate(loginBody);
String tenantId = loginBody.getTenantId();
String username = loginBody.getUsername();
String password = loginBody.getPassword();
String code = loginBody.getCode();
String uuid = loginBody.getUuid();

boolean captchaEnabled = captchaProperties.getEnable();
// 验证码开关
if (captchaEnabled) {
    validateCaptcha(tenantId, username, code, uuid);
}

加载用户信息,校验用户密码,构建 LoginUser 对象。

java
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
    SysUserVo user = loadUserByUsername(username);
    loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
    // 此处可根据登录用户的数据不同 自行创建 loginUser
    return loginService.buildLoginUser(user);
});
loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType());

生成 Token

使用 LoginHelper.login 方法生成 Token,并设置相关的登录模型参数(如设备类型、超时时间等)。

java
SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token
LoginHelper.login(loginUser, model);

返回结果

将生成的 Token 和其他登录信息封装到 LoginVo 对象中,并返回给客户端。

java
LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId());
return loginVo;
编程洪同学服务平台是一个广泛收集编程相关内容和资源,旨在满足编程爱好者和专业开发人员的需求的网站。无论您是初学者还是经验丰富的开发者,都可以在这里找到有用的信息和资料,我们将助您提升编程技能和知识。
专业开发
高端定制
售后无忧
站内资源均为本站制作或收集于互联网等平台,如有侵权,请第一时间联系本站,敬请谅解!本站资源仅限于学习与参考,严禁用于各种非法活动,否则后果自行负责,本站概不承担!