详解若依框架微服务登录注册认证模块ruoyi-auth
必备技能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 负责处理用户的请求并调用后端代码(通常是服务层或业务逻辑层)来处理业务需求。看一下若依是如何在控制器中分发路由的。
@RestControllerpublicclassTokenController{@AutowiredprivateTokenService tokenService;@AutowiredprivateSysLoginService sysLoginService;// R<?> 标明需要返回的数据格式@PostMapping("login")publicR<?>login(@RequestBodyLoginBody form){// 用户登录LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());// 获取登录tokenreturnR.ok(tokenService.createToken(userInfo));}@DeleteMapping("logout")publicR<?>logout(HttpServletRequest request){String token =SecurityUtils.getToken(request);if(StringUtils.isNotEmpty(token)){String username =JwtUtils.getUserName(token);// 删除用户缓存记录AuthUtil.logoutByToken(token);// 记录用户退出日志
sysLoginService.logout(username);}returnR.ok();}@PostMapping("refresh")publicR<?>refresh(HttpServletRequest request){LoginUser loginUser = tokenService.getLoginUser(request);if(StringUtils.isNotNull(loginUser)){// 刷新令牌有效期
tokenService.refreshToken(loginUser);returnR.ok();}returnR.ok();}@PostMapping("register")publicR<?>register(@RequestBodyRegisterBody registerBody){// 用户注册
sysLoginService.register(registerBody.getUsername(), registerBody.getPassword());returnR.ok();}}
@RestController 注解表示生成resfull风格的路由,同时也表明该类是一个 Spring 的 Controller,可以处理 HTTP 请求。
@Autowired 注解表示自动注入依赖,也就是以类型的方式识别Bean,在当前的控制器中可以调用依赖中的方法。
@PostMapping(“login”) 用来处理Post类型的请求,匹配到login请求后进入对应的方法中。
***@DeleteMapping(“logout”)***用于处理 DELETE 类型的请求,匹配到logout请求后进入对应的方法中。
业务实现(Service)
登录业务
控制器中的login方法调用业务层(service)的具体实现,包括用户名密码校验、IP地址校验、用户信息校验、登录日志记录等:
流程图如下:
业务层实现:
publicLoginUserlogin(String username,String password){// 用户名或密码为空 错误if(StringUtils.isAnyBlank(username, password)){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"用户/密码必须填写");thrownewServiceException("用户/密码必须填写");}// 密码如果不在指定范围内 错误if(password.length()<UserConstants.PASSWORD_MIN_LENGTH|| password.length()>UserConstants.PASSWORD_MAX_LENGTH){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"用户密码不在指定范围");thrownewServiceException("用户密码不在指定范围");}// 用户名不在指定范围内 错误if(username.length()<UserConstants.USERNAME_MIN_LENGTH|| username.length()>UserConstants.USERNAME_MAX_LENGTH){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"用户名不在指定范围");thrownewServiceException("用户名不在指定范围");}// IP黑名单校验String blackStr =Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));if(IpUtils.isMatchedIp(blackStr,IpUtils.getIpAddr())){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"很遗憾,访问IP已被列入系统黑名单");thrownewServiceException("很遗憾,访问IP已被列入系统黑名单");}// 查询用户信息R<LoginUser> userResult = remoteUserService.getUserInfo(username,SecurityConstants.INNER);if(StringUtils.isNull(userResult)||StringUtils.isNull(userResult.getData())){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"登录用户不存在");thrownewServiceException("登录用户:"+ username +" 不存在");}if(R.FAIL== userResult.getCode()){thrownewServiceException(userResult.getMsg());}LoginUser userInfo = userResult.getData();SysUser user = userResult.getData().getSysUser();if(UserStatus.DELETED.getCode().equals(user.getDelFlag())){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"对不起,您的账号已被删除");thrownewServiceException("对不起,您的账号:"+ username +" 已被删除");}if(UserStatus.DISABLE.getCode().equals(user.getStatus())){
recordLogService.recordLogininfor(username,Constants.LOGIN_FAIL,"用户已停用,请联系管理员");thrownewServiceException("对不起,您的账号:"+ username +" 已停用");}
passwordService.validate(user, password);
recordLogService.recordLogininfor(username,Constants.LOGIN_SUCCESS,"登录成功");return userInfo;}
注册业务
控制器中的register方法调用业务层(service)的具体实现,包括用户名密码校验、用户是否已注册等:
流程图如下:
业务层实现:
publicvoidregister(String username,String password){// 用户名或密码为空 错误if(StringUtils.isAnyBlank(username, password)){thrownewServiceException("用户/密码必须填写");}if(username.length()<UserConstants.USERNAME_MIN_LENGTH|| username.length()>UserConstants.USERNAME_MAX_LENGTH){thrownewServiceException("账户长度必须在2到20个字符之间");}if(password.length()<UserConstants.PASSWORD_MIN_LENGTH|| password.length()>UserConstants.PASSWORD_MAX_LENGTH){thrownewServiceException("密码长度必须在5到20个字符之间");}// 注册用户信息SysUser sysUser =newSysUser();
sysUser.setUserName(username);
sysUser.setNickName(username);
sysUser.setPassword(SecurityUtils.encryptPassword(password));R<?> registerResult = remoteUserService.registerUserInfo(sysUser,SecurityConstants.INNER);if(R.FAIL== registerResult.getCode()){thrownewServiceException(registerResult.getMsg());}
recordLogService.recordLogininfor(username,Constants.REGISTER,"注册成功");}
上述就是若依的登录注册流程,这中间还涉及到一些通用的工具类,可自行参考若依工具类代码。
版权归原作者 铁锅炖大鹅(e) 所有, 如有侵权,请联系我们删除。