【springcloud】一些项目结构模式

本文详细介绍了在SpringBoot项目中实现登录功能的结构设计,包括Controller、Service、Request对象和各组件间的交互,如密码验证、Token生成与管理以及远程调用用户服务。
摘要由CSDN通过智能技术生成

springboot的一些项目结构模式,代码写法梳理

1 auth 登录有关服务

该示例演示了对于登录功能的处理。
目录结构:

  • controller
    • LoginController
  • domain
    • LoginRequest
  • service
    • impl
      • DefaultLoginService 登录方法
    • LoginService
    • PasswordService 密码校验
    • TokenService 身份验证

各类主要代码如下:

1.1 LoginController

@Slf4j
@RestController
@ResponseAdvice
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private LoginService loginService;

    @PostMapping("/")
    public String login(@RequestBody LoginRequest request) {
        return loginService.login(request);
    }
}

1.2 LoginRequest

LoginRequest的实体类,表示登录请求的数据。实现了Serializable接口,意味着它可以被序列化。类中有三个私有字段:username表示用户名,password表示密码,vcode表示验证码。

@Data
public class LoginRequest implements Serializable {

    private String username;

    private String password;

    private String vcode;
}

使用@Data注解,Lombok会在编译时为该类的所有非静态字段生成以下方法:

getter方法(如public String getUsername())
setter方法(如public void setUsername(String username))
toString() 方法 equals() 和 hashCode() 方法
默认构造函数(如果没有其他构造函数)

1.3 LoginService

接口,声明一个login方法处理请求

public interface LoginService {
    String login(LoginRequest request);
}

1.4 DefaultLoginService 登录校验

LoginService接口的实现,注入1.7 UserServiceClient用于获取用户信息,1.5 PasswordService用于校验密码,1.6 TokenService用于生成和校验token

@Slf4j
@Service("loginService")
public class DefaultLoginService implements LoginService {

    @Resource
    private UserServiceClient userServiceClient; // 用户服务客户端

    @Resource
    private PasswordService passwordService; // 密码服务

    @Resource
    private TokenService tokenService; // Token服务

    /**
     * 处理用户登录请求。
     *
     * @param request 包含登录所需信息的请求对象,如用户名和密码。
     * @return 经过验证的用户token。
     */
    public String login(LoginRequest request) {
        // 通过用户名获取用户信息
        ResponseWrapper<SysUser> userWrapper = userServiceClient.getUser(request.getUsername());
        SysUser user = userWrapper.getData();
        // 断言判断用户存在且未被禁用或删除,如果false会抛出异常消息
        Assert.nonNull(user, USER_NOT_EXIST);
        Assert.isFalse(user.getBlocked(), USER_BLOCKED);
        Assert.isFalse(user.getDeleted(), USER_DELETED);
        // 验证密码
        passwordService.validate(user, request.getPassword());
        // 生成并返回token
        return tokenService.validate(user);
    }

}


1.5 PasswordService 密码验证&用户锁定

@Service //该类为服务组件
public class PasswordService {

    @Autowired //注入Redis服务
    private RedisService redisService;

    /**
     * 验证用户密码是否正确,并管理密码验证失败的重试次数,以防止暴力破解。
     * 
     * @param user 表示需要验证的用户对象。
     * @param password 用户输入的密码。
     * @throws ApiException 如果用户被锁定或者密码不正确时抛出。
     */
    public void validate(SysUser user, String password) {
        String username = user.getUsername();
        // 构造重试次数的Redis键名
        String retryKey = retryCountKeyPrefix + username;
        // 从Redis获取或默认设置重试次数
        Integer retryCount = redisService.getOrDefault(retryKey, 0, Integer.class);
        // 确保重试次数未超过最大值,否则抛出用户锁定异常
        Assert.isTrue(retryCount < maxRetryCount, USER_LOCKED);
        // 当重试次数达到最大值时,锁定用户
        if (retryCount >= maxRetryCount) {
            // 在Redis中设置用户锁定时间
            redisService.set(lockTimeKeyPrefix + username, maxLockTime);
            throw new ApiException(ResponseCode.USER_LOCKED); // 抛出用户锁定异常
        }
        if (!verify(password, user.getPassword())) { // 验证密码失败
            redisService.set(retryKey, retryCount + 1); // 更新重试次数
            throw new ApiException(ResponseCode.PASSWORD_INCORRECT); // 抛出密码不正确异常
        } else { // 密码验证成功
            redisService.delete(retryKey); // 清除重试次数
        }
    }


    /**
     * 验证输入的密码是否与存储的密码相等。
     * 
     * @param input 用户输入的密码。
     * @param saved 存储的密码。
     * @return 返回一个布尔值,表示密码是否匹配。
     */
    private boolean verify(String input, String saved) {
    // 使用Objects静态工具类的equals方法比较两个字符串是否相等
        return Objects.equals(input, saved); 
    }
}

1.6 TokenService 身份验证&token管理

@Service
public class TokenService {

    @Resource
    private RedisService redisService; // 注入Redis服务,用于token的存储和查询

    /**
     * 验证用户登录状态,如果不存在有效token,则创建新的token并返回。
     * 
     * @param user 用户信息
     * @return 返回有效的token字符串
     */
    public String validate(SysUser user) {
        String key = loginKeyPrefix + user.getId(); // 生成用户登录key
        // 查询缓存中是否存在有效的token
        String token = redisService.get(key);
        // 如果token不存在或已失效,则重新生成并保存token
        if (Objects.isNull(token) || !JwtTokenUtils.isValid(token)) {
            redisService.delete(key); // 删除失效的token
            token = JwtTokenUtils.generateToken(user.getUsername(), user); // 生成新的token
            redisService.set(key, token, expiration); // 新token保存至缓存
            return token;
        }
        // 存在有效token时,检查是否需要刷新
        refresh(key);
        return token;
    }

    /**
     * 刷新token,当token剩余有效期超过设定时间的1/5时,更新token有效期。
     * 
     * @param key 用户的token存储key
     */
    private void refresh(String key) {
        // 获取当前token的剩余有效期
        long toExpired = redisService.getTime(key);
        // 判断是否需要刷新token的有效期
        if ((expiration * ratio) > toExpired) {
            redisService.expire(key, expiration); // 更新token有效期
        }
    }

}

1.7 UserServiceClient 远程调用

@FeignClient(name = "system")
//@FeignClient(name = "system", url = "http://localhost:11001")
// 用户服务客户端接口。用于通过Feign进行远程调用用户服务。
public interface UserServiceClient {
    /**根据用户名获取用户信息。
     * 
     * @param username 用户名,用于查询特定用户的信息。
     * @return 返回用户信息的响应包装器。响应包装器中包含具体的用户信息对象SysUser。
     */
    @GetMapping("/user/{username}")
    ResponseWrapper<SysUser> getUser(@PathVariable("username") String username);
}

2

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值