当前位置: 首页 > news >正文

Java框架快速入门X44: Spring Security+OAuth2之授权机制与安全表达式实战

纲要

  • 认证与授权的区别
  • AccessDecisionManager核心接口及其decide方法
  • AccessDecisionVoter投票器接口及三种决策策略
  • RoleVoter角色投票器与ROLE_前缀机制
  • 常用安全表达式速览(hasRolehasAuthoritypermitAlldenyAllaccess等)
  • 基于access表达式的细粒度权限控制
  • 实战:限制“用户只能访问自身资源,管理员可访问全部”
  • 自定义权限逻辑封装为 Spring Bean 并在表达式内调用
  • 项目结构、安全配置、控制器及单元测试完整代码

认证与授权

认证解决“你是谁”,授权解决“你能做什么”。例如小区门禁认证通过后,你仍不能进入别人家,只能进入自己家,这便是授权。Spring Security 的授权模型基于投票决策,由AccessDecisionManager统一协调。

授权核心组件

AccessDecisionManager

该接口负责根据安全对象和配置属性做出授权决定,核心方法如下:

voiddecide(Authenticationauthentication,Objectobject,Collection<ConfigAttribute>configAttributes)throwsAccessDeniedException,InsufficientAuthenticationException;

参数中的object是一个安全对象,Spring Security 并没有限制具体类型,可以是FilterInvocation(Web 请求)或MethodInvocation(方法调用),这使得框架可以在不同层面复用相同的投票机制。

投票器与策略

AccessDecisionManager会轮询一组AccessDecisionVoter,根据各投票器返回的结果,结合自身策略决定放行或拒绝。Spring Security 提供了三种内置策略:

策略实现说明
AffirmativeBased只要有一票赞成即通过(默认)
ConsensusBased多数票决定,平局时可配置通过或拒绝
UnanimousBased必须全部赞成票才通过

组件协作流程如下:

Voter (AuthenticatedVoter)Voter (RoleVoter)AccessDecisionManagerFilterSecurityInterceptorVoter (AuthenticatedVoter)Voter (RoleVoter)AccessDecisionManagerFilterSecurityInterceptorloop[遍历所有 Voter]alt[策略判定通过][拒绝]decide(auth, object, config)vote(auth, object, config)ACCESS_GRANTED / DENIED / ABSTAINvote(...)...放行AccessDeniedException

类结构关系如下:

AccessDecisionManager

+decide(Authentication, Object, Collection<ConfigAttribute>) : void

AffirmativeBased

+decide(...) : void

ConsensusBased

+decide(...) : void

UnanimousBased

+decide(...) : void

AccessDecisionVoter

+vote(Authentication, Object, Collection<ConfigAttribute>) : int

RoleVoter

+vote(...) : int

AuthenticatedVoter

+vote(...) : int

RoleVoter 的角色前缀

RoleVoter会检查ConfigAttribute是否以ROLE_开头。在安全配置中编写hasRole('ADMIN')时,框架自动补充前缀,最终匹配的权限字符串为ROLE_ADMINRoleVoter会判断用户持有的GrantedAuthority列表中是否存在该字符串,存在则投赞成,不存在则拒绝;若资源未配置任何角色属性,则投弃权。

安全表达式一览

从 Spring Security 3 开始,可以使用 Spring EL 表达式进行细粒度授权,常用表达式如下:

表达式说明
denyAll拒绝所有访问
permitAll允许所有访问
isAnonymous()是否为匿名用户
isRememberMe()是否通过 Remember-Me 认证
isAuthenticated()是否已认证(匿名返回 false)
isFullyAuthenticated()是否通过完整登录(非 Remember-Me)
hasRole('ADMIN')拥有角色ROLE_ADMIN
hasAuthority('ROLE_ADMIN')拥有指定的权限(前缀需明确写出)
access('表达式')支持 Spring EL 编写复杂逻辑,如hasRole('ADMIN') or authentication.name == #username

配置顺序:匹配范围越广的规则应放在越后面,例如denyAll若放在最前,则所有请求都会立即被拒绝,后续规则将不生效。

实战:细粒度资源授权

场景描述

定义接口/api/users/{username},返回欢迎信息。要求:

  • 管理员可访问任意用户信息;
  • 普通用户仅能访问自己的信息;
  • 未认证或权限不符则拒绝访问。

项目结构

src/main/java/com/example/security/ ├── config │ └── SecurityConfig.java ├── controller │ └── UserController.java ├── service │ └── UserAuthorizationService.java └── SecurityApplication.java src/test/java/com/example/security/ └── UserControllerTest.java

安全配置

packagecom.example.security.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.provisioning.InMemoryUserDetailsManager;importorg.springframework.security.web.SecurityFilterChain;@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassSecurityConfig{@BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp)throwsException{http.authorizeRequests().antMatchers("/api/public").permitAll()// 使用 access 表达式进行复杂授权.antMatchers("/api/users/{username}").access("hasRole('ADMIN') or authentication.name == #username").anyRequest().authenticated().and().httpBasic();// 为方便测试使用 HTTP Basicreturnhttp.build();}@BeanpublicUserDetailsServiceuserDetailsService(){varuser=User.withUsername("zhangsan").password(passwordEncoder().encode("123456")).roles("USER").build();varadmin=User.withUsername("admin").password(passwordEncoder().encode("123456")).roles("ADMIN").build();returnnewInMemoryUserDetailsManager(user,admin);}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}}

控制器

packagecom.example.security.controller;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublicclassUserController{@GetMapping("/api/users/{username}")publicStringgetUserInfo(@PathVariableStringusername){return"Hello, "+username;}}

自定义权限服务(Bean 表达式调用)

当权限逻辑复杂时,可将其抽取为 Spring Bean,在表达式中通过@beanName.method(params)调用。

packagecom.example.security.service;importorg.springframework.security.core.Authentication;importorg.springframework.stereotype.Service;@Service("userAuthz")publicclassUserAuthorizationService{publicbooleanisOwnerOrAdmin(Authenticationauthentication,Stringusername){if(authentication==null||!authentication.isAuthenticated()){returnfalse;}// 管理员角色通过 GrantedAuthority 判断booleanisAdmin=authentication.getAuthorities().stream().anyMatch(a->a.getAuthority().equals("ROLE_ADMIN"));// 或者是资源拥有者booleanisOwner=authentication.getName().equals(username);returnisAdmin||isOwner;}}

此时安全配置可简化为:

.antMatchers("/api/users/{username}").access("@userAuthz.isOwnerOrAdmin(authentication, #username)")

单元测试

packagecom.example.security;importcom.example.security.SecurityApplication;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.security.test.context.support.WithMockUser;importorg.springframework.test.web.servlet.MockMvc;importstaticorg.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;importstaticorg.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest(classes=SecurityApplication.class)@AutoConfigureMockMvcpublicclassUserControllerTest{@AutowiredprivateMockMvcmockMvc;@TestpublicvoidwhenAdminAccessAnyUser_thenOk()throwsException{mockMvc.perform(get("/api/users/zhangsan").with(org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic("admin","123456"))).andExpect(status().isOk());}@Test@WithMockUser(username="zhangsan",roles="USER")publicvoidwhenOwnerAccessOwnInfo_thenOk()throwsException{mockMvc.perform(get("/api/users/zhangsan")).andExpect(status().isOk());}@Test@WithMockUser(username="zhangsan",roles="USER")publicvoidwhenUserAccessOtherInfo_thenForbidden()throwsException{mockMvc.perform(get("/api/users/lisi")).andExpect(status().isForbidden());}}

总结

Spring Security 的授权机制基于投票器模型,AccessDecisionManager调度多个Voter进行决策。通过安全表达式,特别是access,可以编写灵活且与业务逻辑解耦的权限规则。

本文从核心组件到实际编码,完整演示了如何实现“用户只能访问自身资源、管理员可访问全部”的需求,并展示了如何将自定义权限逻辑封装为 Bean 在表达式中调用。

http://www.jsqmd.com/news/1334697/

相关文章:

  • Godot游戏开发:Shaker插件实现屏幕震动效果与参数调优指南
  • Zotero PDF翻译终极指南:如何5分钟内将英文文献一键变中文
  • 2026年成都净水设备行业的品质坚守: 圣创机电以专业赢得认可 - 市场沸点
  • 宇树科技开启IPO询价,90后创始人携员工冲击科创板,具身智能行业迎上市潮
  • iOS 审核4.3 【一切源于机审】
  • 玻璃瓶缺陷可识别裂缝划痕缺口检测数据集VOC+YOLO格式2716张3类别
  • 2026年高口碑免烫衬衫推荐 欧定OWN DREAM实测评价指南 - 互联网科技品牌测评
  • 使用CLIP-ViT-B-16-laion2B-s34B-b88K提高图像分类效率
  • 如何快速部署Label Studio:5分钟掌握开源数据标注工具完整指南
  • 万达酒店**订房渠道是哪个?官网、APP 与小程序权益一致性对比 - 小橘甄选
  • 未来模型压缩方向:从Kimi-K3-mlx-reap160-2bit看MoE剪枝技术的潜力
  • 构建高性能医院信息系统:分布式微服务架构完整解决方案
  • 深入探索CLIP ViT-B/16 - LAION-2B模型的社区资源与支持
  • 2026年成都净水设备领域深耕: 圣创机电全场景净水之道 - 市场沸点
  • 你的个性化法律实践配置文件
  • Unity视频播放:解决VideoPlayer与RawImage不显示的完整指南
  • 2026贵港卫生间防水补漏三品牌公开参数与场景对照:工艺/材料/报价/质保(捷修/宅乐安/居固安) - 家居避坑指南
  • 【burkert宝德代理商-上海国与自动化设备有限公司】 - 资讯在线
  • Akagi麻将AI助手:3分钟快速上手的智能决策终极指南
  • 如何通过韧性工程重塑系统安全:从预防到适应的5个关键转变
  • 完整解锁《鸣潮》游戏体验:一站式模组解决方案终极指南
  • 【亲测免费】 CLIP ViT-B/16 - LAION-2B:引领零样本图像分类的新篇章
  • 戴森球计划工厂蓝图完全指南:3000+蓝图从入门到精通
  • Cocos Creator引擎:一站式跨平台游戏开发解决方案,让创意轻松触达亿万玩家
  • Claude for Legal:终极法律AI助手完全指南,10分钟打造你的专属法律工作流
  • 如何用PVZTools修改器轻松掌控植物大战僵尸?终极免费辅助工具使用教程
  • springboota89l5校服订购系统
  • 2026成都口碑靠谱家装公司选型攻略:行业标准、避坑指南与高适配服务商精选 - 行业观察网
  • 供应链人员怎么通过机构选择CPPS选修方向? - 众智商学院职业教育
  • 机械臂正逆解的过程(python)