SpringBoot+Security+Redis后台管理系统实战:认证授权与缓存配置详解

发布时间:2026/10/30 3:03:41
SpringBoot+Security+Redis后台管理系统实战:认证授权与缓存配置详解 简介这是一套面向Java Web开发者与企业级应用学习者的完整后台管理系统源码基于Spring Boot、MyBatis、Spring MVC、Spring Security与Redis构建适合希望掌握现代Java技术栈协同开发的中高级学习者参考实践。压缩包共326个文件约1.76MB以95个Java源文件为核心业务与配置代码辅以45个JavaScript、33个HTML、19个CSS及多套前端样式与图标资源另有12个XML映射配置、2个YML配置文件、1个SQL建表脚本和1个Markdown说明文档完整呈现了从持久层映射、MVC分层到安全认证与缓存集成的项目结构。资源已有959人学习下载读者可从中了解Spring Security的登录验证与权限控制实现、MyBatis的SQL映射写法、Redis缓存集成方式以及前后端资源组织与配置管理思路适合作为课程设计、毕业项目或企业级后台开发的参考范本。1. 一套能直接跑通的后台管理系统骨架到底值不值得拆后台管理系统是绝大多数 Java 从业者绕不开的第一类真实项目权限、菜单、日志、缓存、分页、导出样样都得有样样都不难但样样拼在一起就容易翻车。这份基于 SpringBoot MyBatis SpringMVC Spring Security Redis 的完整网站后台管理系统源码包价值不在于代码有多高深而在于它把「认证授权 数据持久化 缓存 后台 CRUD」这条主线串成了一条能跑通的链路。适合两类人一是刚学完 SSM 想找一个结构完整、能改能调的练手项目的新手二是需要快速搭一套后台底座、把精力留给业务模块的熟手。下面按「它是什么 → 怎么跑起来 → 权限和缓存怎么配 → 坑在哪 → 怎么验证」的顺序拆能抄的地方直接给代码。2. 先把工程结构和依赖关系摸清楚SpringBoot 打底Security 与 Redis 各管一摊拿到一个后台管理系统源码包最忌讳上来就mvn spring-boot:run。先花十分钟把目录和依赖看清楚后面排错能省几个小时。这套项目的典型分层是 controller / service / mapper / entity / config / utils前端页面放在resources/static或resources/templates下具体取决于它用的是 Thymeleaf 还是前后端分离。判断方法很简单看pom.xml里有没有模板引擎依赖看 controller 返回值是视图名还是ResponseBody的 JSON。2.1 依赖清单与版本对齐SpringBoot 项目最常见的启动失败不是代码写错而是依赖版本打架。先把pom.xml里这几组坐标对齐尤其是 Spring Security 和 SpringBoot 的版本必须同源否则会出现过滤器链加载顺序异常这种玄学问题。!-- 核心依赖版本以父 pom 的 spring-boot-starter-parent 为准 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version !-- 与 SpringBoot 2.x 搭配3.x 项目需换 3.x 版本 -- /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.20/version /dependency这段依赖的关键点有三个spring-boot-starter-security一旦引入全站默认被拦截所有请求都要认证这是新手第一个「页面 302 跳登录」的来源mybatis-spring-boot-starter的版本必须和 SpringBoot 大版本匹配2.x 配 2.3.x、3.x 配 3.0.x混用会在启动时报NoSuchMethodError连接池用 Druid 的话监控页面的账号密码要单独配别用默认的admin/admin直接上线。参数上mybatis的mapper-locations要指向classpath:mapper/*.xmltype-aliases-package指向实体包这两个配错的表现是「Mapper 注入成功但查询报 Invalid bound statement」属于血泪经验级别的常见坑。2.2 配置文件的三块必改项application.yml里真正需要你动手改的只有三块数据源、Redis、MyBatis。其余保持默认即可改多了反而引入不确定性。spring: datasource: url: jdbc:mysql://127.0.0.1:3306/admin_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver redis: host: 127.0.0.1 port: 6379 database: 0 timeout: 3000ms lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.admin.entity configuration: map-underscore-to-camel-case: trueserverTimezone不写会在 MySQL 8 上报时区异常这是老生常谈但每年还有人踩map-underscore-to-camel-case: true让user_name自动映射到userName不开的话所有下划线字段查出来都是 null排查半天以为是 SQL 问题Redis 的timeout建议显式写默认值在某些 Lettuce 版本下会导致连接池拿不到连接时线程长时间挂起。数据库脚本一般在源码包的sql/或db/目录下先建库再导入导入顺序按外键依赖来先字典表后业务表。2.3 启动验证的最小闭环配置改完启动类跑起来后不要急着点页面先做三步验证控制台无APPLICATION FAILED TO START、访问/login能出登录页、用初始化账号能登进去看到首页。这三步过了说明 SpringBoot 容器、Security 过滤器链、数据库连接、模板渲染四条链路都通了。任何一步卡住按「看日志最后 30 行 → 定位是 Bean 创建失败还是 SQL 异常 → 对照配置」的顺序查比盲目搜索高效得多。3. Spring Security 的认证授权链路从登录表单到方法级注解后台系统的核心不是 CRUD是「谁能进哪个页面、谁能点哪个按钮」。这套项目用 Spring Security 做认证授权理解它的过滤器链是绕不过去的。很多人改权限改到怀疑人生本质是没搞清楚请求在哪个过滤器被拦下、认证信息存在哪、授权判断发生在哪一层。3.1 自定义 UserDetailsService 与密码编码器默认的内存用户只能演示真实项目必须从数据库加载用户。核心是实现UserDetailsService把查出来的用户和角色塞进UserDetails。Service public class DbUserDetailsService implements UserDetailsService { Autowired private SysUserMapper userMapper; Autowired private SysRoleMapper roleMapper; Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user userMapper.selectByUsername(username); if (user null) { throw new UsernameNotFoundException(用户不存在); } // 角色必须以 ROLE_ 前缀存入否则 hasRole 判断永远为 false ListString roleCodes roleMapper.selectRoleCodesByUserId(user.getId()); ListGrantedAuthority authorities roleCodes.stream() .map(code - new SimpleGrantedAuthority(ROLE_ code)) .collect(Collectors.toList()); // 第三个参数 true 表示账号未过期四个布尔值按业务需要调整 return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), true, true, true, true, authorities); } }这段代码有两个必须说清的点。第一hasRole(ADMIN)在底层会自动补ROLE_前缀而hasAuthority(ADMIN)不会所以存库时统一加前缀、判断时用hasRole是最不容易出错的做法混用就是权限判断失效的头号原因。第二密码必须用BCryptPasswordEncoder加密存储初始化 SQL 里的明文密码要么在启动时用编码器转一次要么直接在 SQL 里写$2a$10$...的密文否则登录永远提示密码错误。编码器 Bean 要显式声明别指望自动装配。3.2 过滤器链配置与放行规则WebSecurityConfigurerAdapter在新版本已废弃但很多存量项目还在用两种写法都要能看懂。核心是「哪些路径放行、哪些要认证、登录页和登录接口在哪」。Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabled true) // 开启 PreAuthorize public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/login, /css/**, /js/**, /img/**).permitAll() .antMatchers(/admin/**).hasRole(ADMIN) .anyRequest().authenticated() .and() .formLogin() .loginPage(/login) .loginProcessingUrl(/doLogin) .defaultSuccessUrl(/index) .failureUrl(/login?error) .and() .logout().logoutUrl(/logout).logoutSuccessUrl(/login) .and() .csrf().disable(); // 后台模板项目常关前后端分离用 token 方案 return http.build(); } Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }antMatchers的顺序有讲究从上到下匹配permitAll的静态资源必须写在anyRequest().authenticated()之前否则 CSS 也被拦登录页会变成裸 HTML看起来像「样式全丢了」的故障。loginProcessingUrl要和登录表单的action一致不一致的表现是提交后原地刷新、没有任何提示。csrf().disable()在纯后台模板项目里常见但如果项目带文件上传或对外接口关掉 CSRF 要评估风险常见做法是保留 CSRF 并在表单里加隐藏域。3.3 方法级权限与按钮控制页面级权限靠过滤器链按钮级权限靠PreAuthorize。在 Service 或 Controller 方法上加注解配合EnableGlobalMethodSecurity(prePostEnabled true)生效。PreAuthorize(hasRole(ADMIN) or hasAuthority(user:delete)) PostMapping(/user/delete/{id}) public Result deleteUser(PathVariable Long id) { userService.removeById(id); return Result.ok(); }这里hasRole和hasAuthority混用是刻意的角色管粗粒度权限码管细粒度两者是「或」的关系。前端按钮的显示隐藏则通过登录后返回的权限码集合在模板里判断别只靠后端拦——后端拦是安全底线前端隐藏是体验两者都要有。踩坑点在于注解不生效九成是忘了开prePostEnabled或者注解加在了 private 方法、同类内部调用上Spring AOP 代理不到权限判断直接跳过这种「看起来配了其实没生效」的问题最隐蔽。4. Redis 在后台系统里的三个真实用途别只拿来存 sessionRedis 在这类项目里经常被当成「为了用而用」的组件实际上它承担三件具体的事登录态共享、热点数据缓存、验证码与限流。用对了能明显降低数据库压力用错了就是缓存穿透和脏数据的来源。4.1 序列化配置与 key 规范SpringBoot 默认用 JDK 序列化存进 Redis 的值在客户端看是一串乱码排查时非常痛苦。统一改成 JSON 序列化key 用冒号分隔的业务前缀。Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializerObject serializer new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper new ObjectMapper(); mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(mapper); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }activateDefaultTyping是为了反序列化时能还原具体类型不加的话取出来是LinkedHashMap强转会抛ClassCastException这是缓存读取最典型的翻车现场。key 规范建议业务:模块:标识比如auth:token:1001、cache:dict:status好处是排查时能按前缀批量扫也方便设置统一的过期策略。注意keys *在生产环境禁用用scan代替。4.2 缓存字典与热点数据后台系统里字典表、菜单树、配置项这类「读多写少」的数据最适合缓存。常见做法是查询时先读 Redis未命中再查库并回填更新时删缓存而不是改缓存。public ListSysDict listByType(String dictType) { String key cache:dict: dictType; Object cached redisTemplate.opsForValue().get(key); if (cached ! null) { return (ListSysDict) cached; } ListSysDict list dictMapper.selectByType(dictType); // 加随机过期时间避免同一时刻大量 key 集体失效造成缓存雪崩 long expire 1800 new Random().nextInt(300); redisTemplate.opsForValue().set(key, list, expire, TimeUnit.SECONDS); return list; }参数上过期时间给 30 分钟加随机抖动是常规操作更新字典时用redisTemplate.delete(key)删缓存让下次读请求重建比直接 set 新值更安全能避免并发写导致的旧值覆盖。如果字典数据量很小且几乎不变也可以启动时全量加载到本地缓存Redis 只做兜底这属于选型层面的取舍不是所有数据都值得进 Redis。4.3 登录态与验证码的过期策略如果项目是多实例部署session 必须外置到 Redis否则用户在 A 节点登录、请求打到 B 节点就是未登录。Spring Session 的接入只需加依赖和一行配置但要注意 session 的序列化同样要走 JSON且过期时间要和 Security 的 session 超时对齐。验证码则是另一个典型场景生成后存 Rediskey 用captcha:{uuid}过期 2 到 5 分钟校验成功后立即删除防止重放。这两个场景的共同点是「短生命周期 强一致读」和字典缓存的长生命周期形成对比配置时别用同一套过期策略一刀切。5. 避坑与排查这几处翻车点几乎每个后台项目都会遇到5.1 登录后一直跳回登录页现象是输入正确账号密码页面刷新后仍停在登录页日志里没有明显异常。原因通常是认证成功但 session 没建立或者defaultSuccessUrl配的路径又被权限规则拦截。解决顺序先看loginProcessingUrl与表单 action 是否一致再确认defaultSuccessUrl的目标路径在放行或已认证范围内最后检查是否多实例部署但 session 没外置。三者排除完基本能定位。5.2 Mapper 注入成功但查询报 Invalid bound statement现象是启动正常、Service 里 mapper 不为 null一调用就抛绑定异常。原因是mapper-locations路径没匹配上或者 XML 的 namespace 与接口全限定名不一致或者方法名和 XML 的 id 对不上。解决确认classpath:mapper/*.xml与实际目录一致检查 XML 头部 namespace 是否精确到接口包名方法名逐字比对大小写都算。5.3 Redis 取出的对象强转失败现象是ClassCastException: LinkedHashMap cannot be cast to Xxx。原因是用了默认 JDK 序列化或 JSON 序列化没开类型信息。解决按 4.1 的配置开启activateDefaultTyping并确保读写用的是同一个RedisTemplateBean别一个地方用StringRedisTemplate、另一个地方用自定义模板序列化方式不一致必然出错。5.4 权限注解加了却不生效现象是PreAuthorize标注的方法任何角色都能调。原因有三没开prePostEnabled、注解加在 private 方法上、同类内部调用绕过代理。解决确认配置类有EnableGlobalMethodSecurity(prePostEnabled true)注解只加在 public 的 Controller/Service 方法上跨方法调用走注入的 Bean 而不是this.调用。5.5 静态资源 404 或样式丢失现象是登录页能打开但没样式控制台一堆 404。原因是 Security 拦截了静态资源或resources/static目录结构不符合 SpringBoot 约定。解决在过滤器链里放行/css/**、/js/**、/img/**并确认文件确实在src/main/resources/static下而不是放在webapp里——后者在 SpringBoot 内嵌容器下默认不生效。6. 用接口自测和日志把整条链路验一遍改完权限和缓存别靠点页面判断对错用接口和日志做闭环验证更可靠。我一般会写一个最小的自测脚本把「未登录访问受保护接口 → 登录拿 session → 带 session 再访问 → 无权限角色访问管理接口」四条路径跑一遍任何一条不符合预期就说明配置有问题。# 1. 未登录访问受保护接口预期 302 跳登录 curl -i http://127.0.0.1:8080/admin/user/list # 2. 登录并保存 cookie预期 302 跳首页 curl -i -c cookie.txt -d usernameadminpassword123456 \ http://127.0.0.1:8080/doLogin # 3. 带 cookie 访问预期 200 且返回 JSON 或页面 curl -i -b cookie.txt http://127.0.0.1:8080/admin/user/list # 4. 用普通角色账号访问管理接口预期 403 curl -i -b cookie_user.txt http://127.0.0.1:8080/admin/user/list这四步的价值在于把「认证」和「授权」分开验证第 1 步验过滤器链的放行规则第 2 步验登录接口和密码编码器第 3 步验 session 与权限放行第 4 步验角色粒度。返回码对不上时直接看应用日志里 Security 的 debug 输出把logging.level.org.springframework.securityDEBUG打开过滤器链的每一次放行和拒绝都会打出来比猜快得多。再补一个缓存验证的习惯改完字典数据后先redis-cli看对应 key 是否被删再请求接口看是否回源查库最后看新值是否写回。这三步能确认「删缓存 回填」的逻辑真的生效而不是被本地缓存或浏览器缓存骗了。参数上验证时把过期时间临时调短到 60 秒观察自动失效行为确认无误再改回 30 分钟。从那以后我每次拿到这类后台管理系统源码都强制先跑一遍这四条 curl 和一次缓存读写验证再动业务代码——底座没验通就往上堆功能后面出的问题根本分不清是新代码还是老配置。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询