☰
Spring Boot集成Spring Security构建RBAC权限管理系统实战
2026/10/15 22:46:38 网站建设 项目流程

1. 项目背景与整体设计思路

1.1 这个权限系统解决了什么问题

“蓝鲸云”这个项目的立项背景很直接:公司内部多个业务系统陆续上线,每个系统各搞一套登录和权限逻辑,有的用拦截器,有的用过滤器,有的压根没做权限控制,页面上的按钮谁都能点。时间一长,账号管理混乱、权限失控、审计追溯无从下手。

当时摆在面前的无非两条路:要么继续缝缝补补,要么推倒重来,做一个统一的认证与授权中心,把散落的业务系统接入进来。权衡之后选择了后者,这就是基于Spring Boot和Spring Security搭建后台权限管理系统的由来。

这个系统要解决的核心问题有三类:

  • 认证问题:确认“你是谁”。所有业务系统不再各自维护密码逻辑,统一走蓝鲸云的登录入口。
  • 授权问题:确认“你能干什么”。通过角色和权限的绑定,精确控制用户能访问哪些接口、看到哪些按钮。
  • 审计问题:确认“你干了什么”。记录关键操作日志,出了问题能回溯。

对于正在学习Spring Boot和Spring Security的开发者来说,这类系统的价值在于,它几乎涵盖了企业级后台开发的所有高频需求,而且技术选型主流、社区资料丰富,从零搭建一遍能抵得上翻十篇零散博客。

1.2 为什么选Spring Security而不是Shiro

技术选型时,相信每个人都会纠结过Shiro和Spring Security。我当初也做过对比测试,这里直接说结论。

Shiro的优势是轻量、上手快、配置简单,API设计直观,对于小项目或者非Spring生态的项目非常友好。但它的劣势也很明显:和Spring Boot的整合虽然能用,但不是原生支持,很多高级特性需要自己手动扩展,社区活跃度和Spring Security相比差了一个量级。

Spring Security的优点是:

  • 原生深度整合Spring生态,自动配置能力强,大部分场景只需要写配置类,不用从零手撸过滤器链。
  • 对OAuth2、JWT、LDAP、CAS等主流协议有现成支持,后续如果要对接第三方登录或单点登录,扩展成本低。
  • 基于过滤链的架构设计非常清晰,安全过滤器的执行顺序可以通过配置精确控制,排查问题时有迹可循。
  • 注解支持完善,方法级权限控制用@PreAuthorize一行搞定。

Spring Security的缺点是学习曲线比Shiro陡峭,初次接触容易被过滤器链、AuthenticationManager、SecurityContext这些概念绕晕。但一旦理解了整个调用流程,你会感受到它在复杂场景下的设计深度。

蓝鲸云最终选型Spring Security,核心原因有两个:一是这套系统后续必然要对接多个业务子系统的SSO需求,Spring Security的扩展点在社区里几乎都能找到参考;二是公司技术栈全面转向Spring Boot,选原生集成的安全框架,后续维护成本最低。

1.3 权限模型的设计:RBAC三张表还是五张表

权限系统最核心的就是数据模型设计。蓝鲸云采用经典RBAC(基于角色的访问控制)模型,落库时做了细分。

最基础的RBAC模型是三张表:用户表、角色表、用户角色关联表。这是入门教程的标准写法,能满足“给用户分配角色”这种粗粒度授权。但实际业务中,“角色”这个粒度太粗了。比如财务角色和管理员角色都能访问财务报表页面,但财务角色只能看不能导出,管理员角色既能看也能导出。如果只到角色层级,这种差异没法表达。

所以在三张表的基础上扩展成五张表:

  • 用户表:存账号、密码(BCrypt加密后的密文)、状态、最后登录时间等。
  • 角色表:存角色编码和名称,比如ADMIN、FINANCE、OPERATOR。
  • 菜单权限表:这里要细分一下,菜单资源对应页面路由,按钮权限对应接口操作,两者分开存,但共享同一张权限表的语义。
  • 用户角色关联表:确认用户拥有哪些角色。
  • 角色菜单关联表:确认角色能访问哪些菜单和按钮。

权限表里有一个type字段,用数字区分是目录、菜单还是按钮。目录是顶级导航、菜单是二级页面、按钮是页面上的操作权限。这样前后端渲染菜单树时直接查库,不用硬编码。用户的最终权限集合,是“用户角色”再叠加“角色权限”计算出来的集合。

这个设计的好处是:新业务系统接入时,不需要改蓝鲸云的代码,只需要在菜单权限表里注册自己的页面和按钮,再创建角色并绑定权限,即可上线。整个权限体系对业务系统来说是透明的,业务系统只关注自己内部的数据权限,访问权限全部由蓝鲸云统一收口。

2. 核心认证流程与Spring Security关键配置

2.1 Security过滤器链的搭建思路

Spring Security的核心机制是过滤器链,一个请求到达应用时,要经过一长串过滤器的层层校验,最终才能到达Controller。默认情况下Spring Boot会帮我们装配一条默认的过滤器链,但实际项目中几乎都会自定义。

蓝鲸云的过滤器链配置要点如下:

  • 放行登录接口、验证码接口、静态资源。这些不需要认证就能访问。
  • 其他所有接口默认都需要认证。
  • 自定义一个JWT认证过滤器,插在UsernamePasswordAuthenticationFilter之前或之后,保证每个请求都会带上token,由这个过滤器解析用户身份。
  • 异常处理:配置AuthenticationEntryPoint处理未认证(401),配置AccessDeniedHandler处理无权限(403),返回统一JSON格式,而不是Spring Security默认的重定向页面。
  • 跨域配置:前后端分离项目必配,CORS的配置要放在过滤器链的最前面,否则浏览器会拦截。

关键代码如下,这是配置类的核心骨架:

@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .cors().and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests(auth -> auth .antMatchers("/api/auth/login", "/captcha", "/doc.html").permitAll() .antMatchers("/api/users/**").hasRole("ADMIN") .anyRequest().authenticated() ) .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint()) .accessDeniedHandler(restAccessDeniedHandler()) .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }

这里有两个很容易踩的坑。

第一,sessionCreationPolicy设置成STATELESS后,Spring Security不再创建HttpSession,也不会从Session里读取已认证信息。这就意味着每次请求都必须走JWT过滤器来还原身份信息,需要确保JWT过滤器前面的放行规则不会漏掉受保护的资源。

第二,antMatchers的匹配规则是从上往下执行的,先声明的优先。所以要把最具体的规则放前面,比如先配指定路径再配anyRequest,如果顺序反了,后面的规则永远匹配不到。

2.2 用户认证的核心链路拆解

认证流程是权限系统最重要的主链路。蓝鲸云的登录认证链路设计如下:

  1. 前端提交账号、密码、验证码到 /api/auth/login。
  2. 后端先校验验证码,验证码通过后再调用AuthenticationManager执行认证。
  3. AuthenticationManager内部根据配置找到对应的AuthenticationProvider,这里自定义了一个JwtAuthenticationProvider。
  4. Provider调用UserDetailsService.loadUserByUsername方法从数据库加载用户,拿到密文密码。
  5. 使用BCryptPasswordEncoder.matches方法对比明文密码和密文密码,匹配成功则构建一个Authentication对象,并标记为已认证。
  6. 认证成功后,根据用户ID和角色生成JWT Token,返回给前端。
  7. 前端把Token存储起来,后续每个请求在Header中带上“Authorization: Bearer xxx”。
  8. 请求进入JWT过滤器,解析Token,校验签名和过期时间,从Token中提取用户名,加载用户信息,重新构建Authentication对象并放入SecurityContextHolder。

这里非常关键的机制是SecurityContextHolder。它是Spring Security存储“当前请求用户身份”的容器。认证成功后,后续的Controller代码、Service代码,都通过SecurityContextHolder.getContext().getAuthentication()来获取当前用户的信息。如果提前在Service层注入了UserDetailsService也没用,必须经过过滤器链才能填充SecurityContextHolder。

在实际项目中,我遇到过很多次“拿不到登录用户”的问题,排查下来多半是Controller写了@AuthenticationPrincipal注解但类型没对上,或者过滤器顺序配置有问题导致SecurityContextHolder还没被填充就进入了Controller。理解了这个主链路,排查思路会清晰很多。

2.3 密码加密方案:为什么只认BCrypt

密码存储是权限系统绝不能糊弄的环节。蓝鲸云从第一天起就只用BCryptPasswordEncoder,没有给系统留过明文密码的逃生通道。

BCrypt的特点:

  • 加盐哈希:每次加密同一个密码会得到不同的密文,因为有随机盐参与。这意味着用户表中即使两条记录的密码相同,密文也不同,防止通过密文对比推断密码。
  • 内置成本因子:默认是10,代表2的10次方次哈希运算。成本因子越大,哈希计算越慢,暴力破解的成本越高。生产环境建议设在10到12之间,兼顾安全和性能。
  • 自带抗暴力破解设计:BCrypt算法设计上故意放慢了速度,使得哈希碰撞和彩虹表攻击的成本大幅上升。

很多初级项目用MD5加固定盐,这种方式其实很危险。MD5本身就是为速度设计的,GPU跑MD5每秒能算几十亿次,配合彩虹表,短密码几乎秒破。即使加了盐,盐如果固定写死在代码里,泄露源码就等于泄露全部密码。所以只要项目还在开发阶段,就建议直接用BCrypt,不要走弯路。

在使用BCrypt时,UserDetailsService里有一个容易出错的细节。loadUserByUsername方法返回的UserDetails里的密码必须是从数据库查出来的密文,而不是加密之前的明文。判断密码是否正确是交给PasswordEncoder来执行的,不能在Service代码里手动matches两次。如果自己手写matches逻辑,很容易把逻辑顺序搞错,而且不利于后续更换加密算法。

2.4 JWT生成与校验的细节

JWT是蓝鲸云无状态认证的基础组件。JWT本身就是一个JSON对象经过签名后的字符串,分为Header、Payload、Signature三部分,用点号分隔。

生成Token时,蓝鲸云的Payload里放了三类信息:

  • 用户唯一标识:user_id,方便后续查询用户详细信息。
  • 用户名:username,前端展示时可用。
  • 角色编码集合:roles,方便后续做粗粒度路由判断。

注意事项:Payload里不应该放密码、手机号、身份证等敏感信息。因为JWT的Payload只是做了Base64编码,不是加密的,任何人都可以解码看到内容。如果把敏感信息放进去,等于把用户的隐私明文暴露在Token里,一旦Token被截获,隐私就泄露了。

Token的过期时间设置为2小时,同时支持Refresh Token机制。Access Token过期后,前端拿Refresh Token去换新的Access Token,用户不需要重新登录。Refresh Token的有效期设为7天,存储在Redis中,并且绑定用户ID,销毁时可以直接删掉Redis中的记录,实现“踢人下线”的效果。

校验Token时,JWT过滤器的逻辑分三步:

  1. 从请求头中取出Authorization头。
  2. 判断Token前缀是否为“Bearer”,不是就直接跳过。
  3. 解析签名、校验过期时间,成功则将用户信息写入SecurityContextHolder。

需要特别留意Token解析的异常处理。JWT解析失败时会抛出各种异常,比如签名不一致的SignatureException、过期的ExpiredJwtException、格式非法的MalformedJwtException。这些异常要分门别类处理:签名的失败直接返回401提示重新登录,过期的情况则提示刷新Token,不要统一吞掉异常输出一条笼统的“认证失败”,否则排障时很难定位。

3. 权限控制的落地实现与动态权限设计

3.1 菜单与按钮权限的数据模型

进入权限控制部分的落地实现,也是项目中最花时间的地方。前面提到的五张表,落到具体字段时需要考虑以下几个方面。

用户表(sys_user):

  • id、username、password、status、create_time、update_time
  • status:1启用,0禁用。禁用用户不能登录。

角色表(sys_role):

  • id、role_code、role_name、remark、status

菜单权限表(sys_menu):

  • id、parent_id、menu_name、menu_type、path、component、perms、icon、sort

sys_menu的menu_type分为三种:

  • 目录(0):顶级导航,通常没有路由路径。
  • 菜单(1):页面级路由,对应前端的页面地址。
  • 按钮(2):接口权限点,比如“添加用户”“编辑用户”“删除用户”三个按钮就是三个不同的perms。

perms字段是权限标识符,例如“system:user:add”,这个字符串是前端按钮的唯一标识。前端拿到当前用户的所有perms后,通过Vue的指令或者React的高阶组件来控制按钮的显隐,后端则用@PreAuthorize校验接口的调用权限。前后端双重控制,防止有人绕过界面直接调API。

用户角色关联表(sys_user_role)和角色菜单关联表(sys_role_menu)就是两张中间表,各存两个外键字段即可。

实际开发时,角色的数据权限也要考虑进去。比如同是查看订单的按钮,销售角色只能看自己创建的订单,销售经理角色能看整个部门的订单,老板角色能看全公司订单。这个需求仅靠上面五张表解决不了,所以又加了一个数据范围字段:

  • data_scope:1全部,2本部门及以下,3本部门,4本人

这个字段在角色表里维护,后续业务系统做数据过滤时,从当前用户身上取出这个值,再拼到SQL里。

3.2 动态权限拦截:从@PreAuthorize到动态加载

Spring Security的权限校验有两种方式,一种是注解,一种是自定义过滤器。

注解方式最常用的是@PreAuthorize,写在Controller的方法上:

@PreAuthorize("hasAuthority('system:user:add')") @PostMapping("/users") public Result addUser(@RequestBody SysUser user) { return service.addUser(user); }

这种方式简单直观,代码写死在方法上。但蓝鲸云需要考虑动态扩展:新业务系统接入时,不可能每次都要改主系统的源代码来加权限注解。所以最终采用动态权限加载的方式。

实现思路是自定义一个FilterInvocationSecurityMetadataSource,这个接口负责把一个请求URL映射到对应的权限列表。每次请求到达时,系统根据请求的URL去数据库或者缓存里查询这个URL需要哪些权限,然后再调用自定义的AccessDecisionManager去比对当前用户拥有哪些权限,是否匹配。

完整链路是:

  1. 请求进入过滤器链。
  2. 自定义SecurityMetadataSource根据请求的URI,查找对应权限编码列表。比如请求GET /api/users,查出来需要“system:user:list”权限。
  3. 自定义AccessDecisionManager获取当前用户的权限集合,判断是否包含上述权限编码,因为具体要看是否all-allow规则还是one-allow规则。
  4. 权限不足则抛AccessDeniedException,由统一异常处理返回403。

这个方案的优点是特别灵活:新增接口时,在菜单权限表里加一条记录并绑定角色,立即生效,不需要重新发布系统。缺点是需要保证查询效率,所以权限菜单表的数据要缓存到Redis中,权限变更时主动删除缓存,实现秒级生效。

3.3 前端菜单树的动态渲染

前端的动态菜单渲染是权限系统体验的关键。登录成功后,后端返回当前用户的“菜单树”和“权限标识集合”。前端拿这两份数据做两件事:一是渲染侧边栏菜单,二是控制页面按钮显隐。

后端返回的菜单树结构大致如下:

[ { "id": 1, "menuName": "系统管理", "menuType": 0, "children": [ { "id": 2, "menuName": "用户管理", "menuType": 1, "path": "/system/user", "component": "system/user/index", "perms": "system:user:list", "children": [] } ] } ]

前端拿到这份菜单树,直接递归渲染成侧边栏导航。按钮级别的控制,蓝鲸云用的是自定义Vue指令v-hasPerm,使用方式和v-if一样:

<el-button v-hasPerm="'system:user:add'">新增用户</el-button>

这个指令的实现原理很简单:从Vuex或Pinia中取出当前用户的权限标识数组,如果数组里不包含传入的字符,就把当前DOM节点移除。用指令的好处是性能和语义化都优于在组件里写一堆v-if。

这里容易踩的一个坑是:刷新页面时,Vuex中的permList会被清空,如果侧边栏菜单和按钮权限的获取逻辑没处理好,会出现页面刷新后菜单丢失或按钮全部消失的问题。解决方案有两种,一是把菜单树和权限集合持久化到localStorage,刷新时直接从本地读取;二是在路由的全局前置守卫里请求一次用户信息接口,刷新后确保拿到最新权限。蓝鲸云用的是第二种方案,刷新时总会调用一次/userinfo接口,顺便把用户的基本信息刷新一遍。

3.4 操作日志与登录日志的埋点

权限系统除了管权限,日志审计也是硬需求。蓝鲸云在日志设计上做了两类:登录日志和操作日志。

登录日志记录了每次登录的结果:用户名、登录IP、浏览器UA、登录时间、成功/失败状态、失败原因。这个模块配合安全策略,如果同一账号连续5次登录失败,锁定15分钟,可以防止暴力破解。

操作日志记录的是用户执行的关键操作:谁在什么时间对哪个模块做了什么操作。蓝鲸云用AOP切面来实现,自定义一个@OperLog注解,标记在Controller的方法上:

@OperLog(title = "用户管理", businessType = "删除") @DeleteMapping("/users/{id}") public Result deleteUser(@PathVariable Long id) { return service.deleteUser(id); }

切面里通过环绕通知,在方法执行前后分别记录操作参数和返回结果。关键点是:

  • 请求参数要序列化成JSON字符串存入日志表,但如果参数里包含密码字段,需要在序列化前脱敏,不然密码会明文落库。
  • 操作人的信息从SecurityContextHolder获取,如果切面里取不到当前用户,说明过滤器链执行顺序有问题,优先检查配置。
  • 操作日志的数据量会随着时间急剧膨胀,需要定期归档清理,蓝鲸云的做法是把非当月的日志转入历史表,主表只保留近30天数据。

日志存储这里,起初想用文件追加的方式,后来还是选了数据库存储。文件方式在分布式环境下日志散落各服务器,排查困难;数据库存储配合查询界面的时间筛选、操作人筛选、模块筛选,运维同学用起来舒服得多。

4. 常见问题与排查技巧实录

4.1 前端跨域导致请求被拦截

前后端分离架构下,跨域问题是最常见的拦路虎。开发环境前端跑在8080端口,后端跑在8081端口,浏览器发请求时会产生CORS跨域。

Spring Security的跨域配置经常出问题的点在于:如果只配置了WebMvcConfigurer的addCorsMappings,但没在SecurityConfig里配置cors(),那么Security过滤器链会在CORS处理之前拦截请求,导致跨域配置不生效。正确做法是在SecurityConfig的HttpSecurity配置里调用http.cors(),让它把CorsFilter注册到过滤器链的最前面。

另外,如果启用了自定义CorsFilter,要注意过滤器顺序。OrderedCorsFilter要排在认证过滤器之前,不然预检请求OPTIONS走到认证逻辑,会被401拦截。蓝鲸云在开发环境遇到过这个问题,排查很久才发现是过滤器顺序导致的,后来统一用Spring Security提供的cors()配置,没有再手撸CorsFilter。

4.2 SecurityContextHolder在当前线程拿不到用户信息

这个问题的典型场景是:在Service层写异步任务或者新开线程处理业务时,子线程里调用SecurityContextHolder.getContext().getAuthentication()结果为null。

原因是SecurityContextHolder默认使用ThreadLocal存储认证信息,ThreadLocal只在当前线程内可见。子线程开启后是全新的线程,父线程的ThreadLocal内容不会自动继承。蓝鲸云的项目中,导出的Excel文件功能就踩过这个坑:用户发起导出请求,主线程里权限校验通过了,但导出任务丢到线程池异步执行时,子线程里想通过SecurityContextHolder获取操作人姓名,结果是null。

解决方案有几种:

  • 尽量避免在子线程里访问SecurityContextHolder,把需要的用户信息作为参数传给子线程。
  • 如果想优雅解决,可以配置SecurityContextHolder的策略为MODE_INHERITABLETHREADLOCAL,这是内部支持的策略,让子线程继承父线程的上下文。
  • 如果是Spring的Async线程池,可以通过TaskDecorator来包装Runnable,在提交任务时将父线程的SecurityContext传给子线程。

个人推荐第一种方案,把用户ID和用户名作为参数显式传递,逻辑更清晰,也不用担心线程池复用带来的上下文污染。

4.3 权限变更后旧Token依然有效

这是JWT方案的老大难问题。JWT是无状态的,服务端不存储Token信息,所以用户被修改权限后,已签发的Token在有效期内仍然带着旧的角色和权限信息,如果前端代码从Token里直接解析角色来做判断,那么权限变更无法及时生效。

蓝鲸云的解决思路是“Token只保存用户身份,不保存权限快照”。请求进入JWT过滤器后,解析Token拿到用户ID,然后从Redis缓存中加载这个用户的实时权限集合,再存入SecurityContextHolder。权限变更时,重新计算用户的权限集合并更新Redis缓存,这样旧Token即使没过期,拿到的权限也是最新的。

这个方案的代价是每次请求都要多一次Redis查询。蓝鲸云做了分级处理:核心接口和大部分普通接口都以查库结果为准,少数极高频接口用本地缓存加定时刷新。根据实际压测结果,加了Redis权限查询后,单接口耗时增加不超过10毫秒,对于后台管理系统完全可以接受。

4.4 接口防刷与登录限流

后台管理系统虽然用户量不大,但接口安全不能大意。蓝鲸云在网关层面做了两层保护:

  • IP限流:同一IP的登录接口请求,每分钟不超过20次。
  • 验证码:登录接口强制要求输入图形验证码。
  • 账号锁定:连续5次密码错误,账号锁定15分钟。

限流实现可以用Redisson的RateLimiter,也可以用Sentinel的流控规则。蓝鲸云基于Redisson实现自定义注解@RateLimit,标记在Controller方法上,根据传入参数决定限流的粒度和阈值。这个组件独立封装,后续接入新系统时,只需要在接口上打注解即可,学习成本很低。

4.5 权限表查询性能优化

随着业务系统不断接入,sys_menu表的行数会增长,但一般不会超过几千行。真正影响性能的是每次请求都要查询权限集合。

蓝鲸云做了几层优化:

  • 用户角色权限集合缓存到Redis,key的格式为user:perms:{userId},过期时间72小时。
  • 用户角色变更、角色权限变更时,主动删除相关用户的缓存key,保证实时性。
  • 菜单权限表的“URL到权限编码”的映射关系,启动时加载到本地内存缓存,避免每次请求都查数据库。
  • 权限判断列表采用HashSet结构,contains方法的时间复杂度是O(1)。

这里要提醒的是,缓存不是越复杂越好。初期蓝鲸云用过缓存版本号机制来更新用户权限,结果因为版本号传参遗漏导致线上权限不一致,排查起来极其痛苦。后来简化成主动删除缓存的方式,逻辑反而更稳。

5. 蓝鲸云权限系统的扩展与改造建议

5.1 多系统统一登录的SSO改造

蓝鲸云作为权限中心,天然适合升级为SSO统一登录中心。当一个新系统要接入时,不需要在主系统里改代码,只需要新系统接入OAuth2协议,让用户在蓝鲸云登录后,用Authorization Code换取该系统的Token。

Spring Security对OAuth2的支持可以直接引入spring-security-oauth2-authorization-server,但版本依赖要特别留意,这是独立仓库,和Spring Boot版本并不完全同步。蓝鲸云接入时只在开发环境跑通了两个子系统,生产环境暂时还是内网信任机制,没有全面推开SSO。不过这条路已经验证过可行,后续如果说要扩展,有完整的参考代码可以直接复用。

5.2 数据权限的一体化方案

前面讲到的数据范围字段,在蓝鲸云里只是预留了字段。如果做完整的数据权限,需要在业务系统侧注入拦截器,根据当前用户的数据范围自动追加SQL条件。蓝鲸云目前的做法是封装了一个MyBatis拦截器,给Mapper执行的SQL自动拼接where条件(比如“AND creator_id = {当前用户ID}”)。但这个消息只覆盖逻辑简单的业务表,复杂的聚合查询还是需要业务系统手动处理,拦裁器只处理简单的表前缀匹配。

5.3 并入DevSecOps流水线

蓝鲸云在项目后期,接入了代码扫描工具,每次提交代码后自动扫描安全漏洞,例如SQL注入、XSS、硬编码密码等。扫描结果在流水线中阻断高风险告警,提高代码上线的安全水线。这块对权限系统尤为重要,因为权限系统一旦出漏洞,影响的是所有接入子系统的账号安全。

6. 写在最后的一些个人体会

蓝鲸云这套权限系统,从立项到上线,本质上没有用到特别高深的技术,Spring Boot和Spring Security都是成熟框架。真正花费时间的是业务梳理和细节打磨,角色的划分、权限的粒度、数据范围的设计、日志的审计,这些都要跟不同业务系统反复对齐。技术实现只是把这些规则落地而已。

如果现在有人要重新做一套类似的系统,我会建议先别急着写代码,把一张纸一支笔拿出来,把用户、角色、权限、数据范围这四层关系画清楚,再逐个确认每个后台模块的按钮权限标识怎么编、菜单树怎么分级、数据范围怎么划分。这些设计定了,技术实现就是体力活。

另外两个个人觉得很有价值的细节:一是BCrypt密码加密从第一天就用,不要等上线前再补;二是统一异常处理和统一返回体结构,前后端协作的效率提升是肉眼可见的。权限系统里,凡是走歪门邪道想省事的,后面大概率会花更多时间补窟窿。

蓝鲸云项目的代码目前沉淀在公司内部知识库中,新员工入职第一周,通常是让他照着这套权限系统跑通一遍登录和权限校验的流程。过程中踩的坑、看的源码、改的代码,比读十本理论书收获都大。如果你也在打磨自己的后台权限系统,希望这篇内容能帮上一点忙。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询