【前端必备技能java之若依框架认证(登录注册)模块梳理】


必备技能java系列梳理的文章并不涉及造轮子,以若依框架为基础,分析微服务Spring Cloud的能力,并理清微服务在业务处理上搭建的应用层架构,不会追问技术实践的底层细节,目标是可以让有后端经验的非java相关的程序员可以使用Spring Cloud搭建属于自己的后端服务

认证模块目录结构

请添加图片描述

上面的结构来自于若依认证模块仓库

  • src->main->java目录是SpringCloud项目在生成项目后的固定结构,
  • com.ruoyi.auth为项目自定义,包含了若依认证模块的业务实现,
    1、controller 为项目配置文件
    2、form为登录注册使用的数据对象
    3、service为网关的业务层实现
  • resources目录下为本地配置,
  • target为编译后的文件目录。

为什么需要认证模块

在这里插入图片描述
上面是若依的架构图,从上图可以看出当客户端发起Http请求之后,会首先进入网关,在网关这一层需要做认证处理,这里的认证包括生成Token,用户登录业务、用户注册业务、记录登录信息等。
实际上就是负责用户身份验证和授权,之所以会抽离为单独模块,是为了单独维护这一块的逻辑,方便后续的扩展比如集成第三方认证(OAuth验证),这种业务分割的方式也可以复用到其他类似的模块中。

控制器(controller)

Controller 负责处理用户的请求并调用后端代码(通常是服务层或业务逻辑层)来处理业务需求。看一下若依是如何在控制器中分发路由的。

@RestController
public class TokenController
{
    
    
    @Autowired
    private TokenService tokenService;
    @Autowired
    private SysLoginService sysLoginService;
    // R<?> 标明需要返回的数据格式
    @PostMapping("login")
    public R<?> login(@RequestBody LoginBody form)
    {
    
    
        // 用户登录
        LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());
        // 获取登录token
        return R.ok(tokenService.createToken(userInfo));
    }
    @DeleteMapping("logout")
    public R<?> logout(HttpServletRequest request)
    {
    
    
        String token = SecurityUtils.getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
    
    
            String username = JwtUtils.getUserName(token);
            // 删除用户缓存记录
            AuthUtil.logoutByToken(token);
            // 记录用户退出日志
            sysLoginService.logout(username);
        }
        return R.ok();
    }
    @PostMapping("refresh")
    public R<?> refresh(HttpServletRequest request)
    {
    
    
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser))
        {
    
    
            // 刷新令牌有效期
            tokenService.refreshToken(loginUser);
            return R.ok();
        }
        return R.ok();
    }
    @PostMapping("register")
    public R<?> register(@RequestBody RegisterBody registerBody)
    {
    
    
        // 用户注册
        sysLoginService.register(registerBody.getUsername(), registerBody.getPassword());
        return R.ok();
    }
}

@RestController 注解表示生成resfull风格的路由,同时也表明该类是一个 Spring 的 Controller,可以处理 HTTP 请求。
@Autowired 注解表示自动注入依赖,也就是以类型的方式识别Bean,在当前的控制器中可以调用依赖中的方法。
@PostMapping(“login”) 用来处理Post类型的请求,匹配到login请求后进入对应的方法中。
***@DeleteMapping(“logout”)***用于处理 DELETE 类型的请求,匹配到logout请求后进入对应的方法中。

业务实现(Service)

登录业务

控制器中的login方法调用业务层(service)的具体实现,包括用户名密码校验IP地址校验用户信息校验登录日志记录等:
流程图如下:
请添加图片描述
业务层实现:

  public LoginUser login(String username, String password)
    {
    
    
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password))
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
            throw new ServiceException("用户/密码必须填写");
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
            throw new ServiceException("用户密码不在指定范围");
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
            throw new ServiceException("用户名不在指定范围");
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
        }
        // 查询用户信息
        R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);

        if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
            throw new ServiceException("登录用户:" + username + " 不存在");
        }

        if (R.FAIL == userResult.getCode())
        {
    
    
            throw new ServiceException(userResult.getMsg());
        }
        LoginUser userInfo = userResult.getData();
        SysUser user = userResult.getData().getSysUser();
        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        }
        if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
    
    
            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
        }
        passwordService.validate(user, password);
        recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
        return userInfo;
    }

注册业务

控制器中的register方法调用业务层(service)的具体实现,包括用户名密码校验用户是否已注册等:
流程图如下:
请添加图片描述
业务层实现:

  public void register(String username, String password)
    {
    
    
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password))
        {
    
    
            throw new ServiceException("用户/密码必须填写");
        }
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
    
    
            throw new ServiceException("账户长度必须在2到20个字符之间");
        }
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
    
    
            throw new ServiceException("密码长度必须在5到20个字符之间");
        }

        // 注册用户信息
        SysUser sysUser = new SysUser();
        sysUser.setUserName(username);
        sysUser.setNickName(username);
        sysUser.setPassword(SecurityUtils.encryptPassword(password));
        R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);

        if (R.FAIL == registerResult.getCode())
        {
    
    
            throw new ServiceException(registerResult.getMsg());
        }
        recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功");
    }

上述就是若依的登录注册流程,这中间还涉及到一些通用的工具类,可自行参考若依工具类代码。

猜你喜欢

转载自blog.csdn.net/weixin_42467322/article/details/135992881