若依框架多用户体系架构设计从零构建APP用户模块的实践指南在当今多终端融合的开发场景中一套代码同时支持后台管理系统和移动端用户已成为常态需求。若依RuoYi作为国内广泛使用的开源后台管理系统其前后端分离版本提供了完善的权限管理和用户体系但默认仅内置了后台管理用户SysUser模块。本文将深入探讨如何在保持框架核心优势的同时优雅地扩展一套独立的APP用户体系实现多类型用户并存的架构设计。1. 理解若依用户体系的设计哲学若依框架的用户认证体系建立在Spring Security之上通过LoginUser和TokenService等核心类实现了完整的身份验证流程。要新增用户体系首先需要理解其设计思想用户身份抽象框架将用户身份信息抽象为LoginUser对象包含用户基本属性、权限集合和会话信息令牌机制采用JWTRedis的双重验证机制既保证无状态认证又支持主动失效分层隔离控制层、服务层、数据访问层严格分离遵循单一职责原则关键设计对照表组件类型后台用户实现APP用户实现方案用户实体SysUserAppUser登录身份LoginUserLoginAppUser令牌服务TokenServiceAppTokenService数据访问SysUserMapperAppUserMapper2. 数据库与实体层设计创建独立的APP用户表是扩展的第一步需要注意与原有系统的兼容性CREATE TABLE app_user ( user_id bigint NOT NULL AUTO_INCREMENT COMMENT 用户ID, user_name varchar(30) NOT NULL COMMENT 用户账号, nick_name varchar(30) NOT NULL COMMENT 用户昵称, mobile varchar(11) DEFAULT COMMENT 手机号码, password varchar(100) DEFAULT COMMENT 密码, salt varchar(50) DEFAULT COMMENT 盐, status char(1) DEFAULT 0 COMMENT 帐号状态, del_flag char(1) DEFAULT 0 COMMENT 删除标志, login_ip varchar(128) DEFAULT COMMENT 最后登录IP, login_date datetime DEFAULT NULL COMMENT 最后登录时间, PRIMARY KEY (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENTAPP用户信息表;实体类需要继承框架基础类以保持统一行为public class AppUser extends BaseEntity { private Long userId; private String userName; private String nickName; private String mobile; private String password; private String salt; private String status; private String delFlag; private String loginIp; private Date loginDate; // getters setters }注意事项字段命名尽量与SysUser保持一致密码加密使用相同的策略MD5盐基础字段如create_time、update_time通过继承BaseEntity获得3. 认证体系平行扩展3.1 令牌服务的定制化实现创建AppTokenService复制核心逻辑但使用不同的Redis键前缀Component public class AppTokenService { private static final String LOGIN_APP_TOKEN_KEY login_app_tokens:; public String createAppToken(LoginAppUser loginAppUser) { String token IdUtils.fastUUID(); loginAppUser.setToken(token); refreshToken(loginAppUser); MapString, Object claims new HashMap(); claims.put(Constants.LOGIN_APP_USER_KEY, token); return Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } private String getTokenKey(String token) { return LOGIN_APP_TOKEN_KEY token; } }3.2 安全过滤器的路由区分修改JwtAuthenticationTokenFilter实现双用户体系识别Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { if (request.getRequestURI().startsWith(/app/)) { // APP用户认证流程 LoginAppUser appUser appTokenService.getLoginAppUser(request); if (appUser ! null) { appTokenService.verifyToken(appUser); SecurityContextHolder.getContext() .setAuthentication(buildAuthToken(appUser)); } } else { // 原有后台用户认证流程 LoginUser sysUser tokenService.getLoginUser(request); if (sysUser ! null) { tokenService.verifyToken(sysUser); SecurityContextHolder.getContext() .setAuthentication(buildAuthToken(sysUser)); } } chain.doFilter(request, response); }4. 业务逻辑层的实现策略4.1 服务接口定义保持与原有用户服务相似的接口设计public interface IAppUserService { AppUser selectAppUserByUserName(String userName); int updateAppUser(AppUser appUser); boolean checkPassword(String password, String salt, String hashPwd); default String encryptPassword(String password, String salt) { return DigestUtils.md5DigestAsHex((password salt).getBytes()); } }4.2 密码处理的最佳实践密码安全是用户系统的核心建议采用以下策略注册时生成随机盐值使用不可逆加密框架默认MD5登录时校验密码后立即更新登录信息Service public class AppUserServiceImpl implements IAppUserService { Override public boolean checkPassword(String password, String salt, String hashPwd) { String encrypted encryptPassword(password, salt); return encrypted.equals(hashPwd); } private String generateSalt() { // 生成8位随机字母数字组合 return RandomStringUtils.randomAlphanumeric(8); } }5. 控制层与API设计5.1 登录接口实现RestController RequestMapping(/app) public class AppLoginController extends BaseController { PostMapping(/login) public AjaxResult login(RequestBody LoginAppBody body) { String token appLoginService.login( body.getUsername(), body.getPassword() ); return success().put(Constants.TOKEN, token); } GetMapping(/userInfo) public AjaxResult userInfo() { LoginAppUser appUser SecurityUtils.getLoginAppUser(); return success(appUser.getAppUser()); } }5.2 安全配置调整在SecurityConfig中需要放行APP登录接口Override protected void configure(HttpSecurity http) { http.authorizeRequests() .antMatchers(/app/login).permitAll() .antMatchers(/app/**).authenticated() .and() .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); }6. 前端集成的关键点虽然本文主要关注后端实现但前端集成时需要注意Token存储建议使用localStorage而非cookie请求头设置所有API请求需携带Authorization头路由区分API前缀使用/app/与后台接口隔离示例axios配置const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 }) service.interceptors.request.use(config { if (store.getters.token config.url.startsWith(/app)) { config.headers[Authorization] Bearer store.getters.token } return config })7. 扩展性与维护性考量当系统需要支持更多用户类型时可采用以下模式策略模式定义用户类型处理器接口工厂模式根据请求路径创建对应的认证服务注解驱动使用自定义注解标记不同用户类型的处理器用户类型枚举示例public enum UserType { SYS_USER(sys, 后台用户), APP_USER(app, 移动用户), MERCHANT(mch, 商户用户); private String code; private String desc; // constructor getters }在项目实践中我们发现这种平行扩展架构具有以下优势新用户类型的添加不会影响现有系统各用户体系保持独立的数据隔离可以复用框架的基础设施如日志、监控等权限体系可以灵活配置不同的访问策略