认证授权【Spring Security】
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。由于它是Spring生态系统中的一员因此它伴随着整个Spring生态系统不断修正、升级在spring boot项目中加入spring security更是十分简单使用Spring Security减少了为企业系统安全控制编写大量重复代码的工作。快速使用1. 引入security依赖!-- Spring Security依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId scopeprovided /scope /dependency启动项目控制台自动输出密码。每次项目启动密码都会重新生成。在浏览器输入本地ip端口会跳出一个security默认登录页面这就是 spring Security生效了。默认用户名为user。成功后的页面如下所示基本原理SpringSecurity 本质上是一个过滤器链有很多个过滤器。FilterSecurityInterceptor: 是一个方法级的权限过滤器基本位于过滤链的最底部ExceptionTranslationFilter:是个异常过滤器用来处理在认证授权过程中抛出的异常UsernamePasswordAuthenticationFilter:对 /login的POST请求做拦截, 校验表单中用户名,密码用户认证流程如下图所示客户端进行登录请求Spring Security 的 UsernamePasswordAuthenticationFilter过滤器将请求封装到 Authentication在UserDetailsService类调用loadUserByUsername方法返回一个UserDetails这个UserDetails封装了用户名及加密后的密码等信息获取UserDetails中的密码与Authentication中的密码通过PasswordEncoder进行加密匹配。认证核心组件系统中会有许多用户确认当前是哪个用户正在使用我们系统就是登录认证的最终目的。整个系统安全都是围绕当前登录用户展开的这个不难理解要是当前登录用户都不能确认了那A下了一个订单下到了B的账户上这不就乱套了。这一概念在Spring Security中的体现就是Authentication它存储了认证信息代表当前登录用户。我们在程序中如何获取并使用它呢我们需要通过SecurityContext来获取AuthenticationSecurityContext就是我们的上下文对象这个上下文对象则是交由SecurityContextHolder进行管理可以在程序任何地方使用它Authentication authentication SecurityContextHolder.getContext().getAuthentication();SecurityContextHolder原理非常简单就是使用ThreadLocal来保证一个线程中传递同一个对象Spring Security中三个核心组件​ 1、Authentication存储了认证信息代表当前登录用户​ 2、SeucirtyContext上下文对象用来获取Authentication​ 3、SecurityContextHolder上下文管理对象用来在程序任何地方获取SecurityContextAuthentication包含的信息​ 1、Principal用户信息没有认证时一般是用户名认证后一般是用户对象​ 2、Credentials用户凭证一般是密码​ 3、Authorities用户权限Spring Security 用户认证核心代码如下// 生成一个包含账号密码的认证信息 Authentication authenticationToken new UsernamePasswordAuthenticationToken(username, passwrod); // AuthenticationManager校验这个认证信息返回一个已认证的Authentication Authentication authentication authenticationManager.authenticate(authenticationToken); // 将返回的Authentication存到上下文中 SecurityContextHolder.getContext().setAuthentication(authentication);可以看到上面的核心代码AuthenticationManager 就是Spring Security用于执行身份验证的组件只需要调用它的authenticate方法即可完成认证。Spring Security默认的认证方式就是在UsernamePasswordAuthenticationFilter这个过滤器中进行认证的该过滤器负责认证逻辑。AuthenticationManager校验先根据用户名查出用户信息将查询到的用户密码和当前验证的密码加密后对比比对不成功则爆出异常提示。AuthenticationManager校验逻辑源码如下public Authentication authenticate(Authentication authentication) throws AuthenticationException { ...省略其他代码 // 传递过来的用户名 String username authentication.getName(); // 调用UserDetailsService的方法通过用户名查询出用户对象UserDetail查询不出来UserDetailService则会抛出异常 UserDetails userDetails this.getUserDetailsService().loadUserByUsername(username); // 获取传递过来的密码 String password authentication.getCredentials().toString(); // 使用密码解析器PasswordEncoder传递过来的密码是否和真实的用户密码匹配 if (!passwordEncoder.matches(password, userDetails.getPassword())) { // 密码错误则抛出异常 throw new BadCredentialsException(错误信息...); } // 注意这里返回的已认证Authentication是将整个UserDetails放进去充当Principal UsernamePasswordAuthenticationToken result new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), userDetails.getAuthorities()); return result; ...省略其他代码 }问题1谁来执行查询用户信息的操作呢正常业务都是将用户信息存储在数据库中。这个业务操作就在UserDetailsService中进行UserDetailsService实现了 loadUserByUsernameString username 方法。问题2userDetails 对象有什么属性这里面包含了用户名和密码及其他常用字段但在实际开发中业务不一样实体属性也不一样。所以我们会重新实现该接口。但是这样需要重写很多方法。因此我们可以继承 Security提供的 User类org.springframework.security.core.userdetails.User。public interface UserDetails extends Serializable { /** * 用户权限集合这个权限对象现在不管它到权限时我会讲解 */ Collection? extends GrantedAuthority getAuthorities(); /** * 用户密码 */ String getPassword(); /** * 用户名 */ String getUsername(); /** * 用户没过期返回true反之则false */ boolean isAccountNonExpired(); /** * 用户没锁定返回true反之则false */ boolean isAccountNonLocked(); /** * 用户凭据(通常为密码)没过期返回true反之则false */ boolean isCredentialsNonExpired(); /** * 用户是启用状态返回true反之则false */ boolean isEnabled(); }即操作如下自定义一个User类然后通过继承Security的User类import com.atguigu.model.system.SysUser; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; public class CustomUser extends User { /** * 我们自己的用户实体对象要调取用户信息时直接获取这个实体对象 */ private SysUser sysUser; public CustomUser(SysUser sysUser, Collection? extends GrantedAuthority authorities) { super(sysUser.getUsername(), sysUser.getPassword(), authorities); this.sysUser sysUser; } public SysUser getSysUser() { return sysUser; } public void setSysUser(SysUser sysUser) { this.sysUser sysUser; } }实操用户认证设置登录的用户名和密码方式一配置文件设置在上面我们通过security默认的用户名和密码登录了。现在我们想要自定用用户名和密码如何操作呢在SpringBoot项目中可以在 .properties 配置文件中指定用户名和密码。再次启动项目发现控制台不再输出密码了。spring.security.user.nameadmin spring.security.user.password123方式二配置类设置自定义一个配置类配置类内容如下之后重新测试Configuration public class SecurityConf extends WebSecurityConfigurerAdapter { Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 密码加密 BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder(); String password passwordEncoder.encode(123); auth.inMemoryAuthentication().withUser(lucy).password(password).roles(admin); } Bean PasswordEncoder password() { return new BCryptPasswordEncoder(); } }方式三自定义设置1. 创建配置类设置使用哪个userDetailsService实现类Configuration public class SecurityConf2 extends WebSecurityConfigurerAdapter { Autowired private UserDetailsService userDetailsService; Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(password()); } Bean PasswordEncoder password() { return new BCryptPasswordEncoder(); } }2. 编写实现类返回User对象User对象有用户名密码和操作权限Service(userDetailsService) // 将配置类中注入的UserDetailsService设定的名称在此引入 public class MyUserDetailService implements UserDetailsService { Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { ListGrantedAuthority auths AuthorityUtils.commaSeparatedStringToAuthorityList(role); return new User(lisi, new BCryptPasswordEncoder().encode(123), auths); } }上面的方式将用户名和密码固定死了真实的业务场景肯定是从数据库中查询用户信息的那么接下来将使用自定义的方式改造代码从数据库中获取用户信息。改进版本1. 自定义加密方法新建一个CustomMd5PasswordEncoder类实现 Security的 PasswordEncoder重写encode方法 这里采用MD5进行加密。import com.atguigu.common.util.MD5; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; /** * 密码处理 * */ Component public class CustomMd5PasswordEncoder implements PasswordEncoder { public String encode(CharSequence rawPassword) { return MD5.encrypt(rawPassword.toString()); } public boolean matches(CharSequence rawPassword, String encodedPassword) { return encodedPassword.equals(MD5.encrypt(rawPassword.toString())); } }自定义了CustomMd5PasswordEncoder现在需要告诉 Spring Security用我的加密规则别用默认的。使用的地方在SecurityConfig配置类中。Configuration public class SecurityConf2 extends WebSecurityConfigurerAdapter { Autowired private UserDetailsService userDetailsService; Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(password()); } Bean PasswordEncoder password() { // 原来return new BCryptPasswordEncoder(); // 现在换成自定义的 return new CustomMd5PasswordEncoder(); // 使用你自定义的 MD5 加密 } }2. 添加CustomUser对象import com.atguigu.model.system.SysUser; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; public class CustomUser extends User { /** * 我们自己的用户实体对象要调取用户信息时直接获取这个实体对象 */ private SysUser sysUser; public CustomUser(SysUser sysUser, Collection? extends GrantedAuthority authorities) { super(sysUser.getUsername(), sysUser.getPassword(), authorities); this.sysUser sysUser; } public SysUser getSysUser() { return sysUser; } public void setSysUser(SysUser sysUser) { this.sysUser sysUser; } }CustomUser在 UserDetailsService 的实现类中被创建并返回。3. 实现 UserDetailsService 接口Service(userDetailsService) public class MyUserDetailService implements UserDetailsService { Autowired private UserMapper userMapper; Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 根据用户名查询数据库 User sysUser userMapper.selectByUsername(username); if (sysUser null) { return null; // 如果查不到用户返回null,由provider来抛异常 // throw new UsernameNotFoundException(用户名不存在); } // 2. 查询用户的权限列表 //ListString permissions userMapper.selectPermissionsByUserId(sysUser.getId()); // 3. 将权限转换为 GrantedAuthority 集合 // ListGrantedAuthority authorities permissions.stream() // .map(SimpleGrantedAuthority::new) // .collect(Collectors.toList()); // 4. 关键在这里使用 CustomUser return new CustomUser(sysUser, Collections.emptyList()); // 这里先返回一个空的权限 //return new CustomUser(sysUser, authorities); //第二个参数就是权限相关的 } }AuthenticationManager校验所调用的三个组件就已经做好实现了。此时我们可以在默认的登录页面查看效果。用户授权基于权限控制方法1使用 hasAuthority方法如果当前的主体具有指定的权限则返回true否则返回 false。源码如下所示1. 在配置类设置当前访问地址有哪些权限2. 在UserDetailsService实现的接口中把返回User对象设置权限方法2使用 hasAnyAuthority方法可以添加多个角色源码如下基于角色控制方法1使用 hasRole方法security会自动在角色上添加ROLE_前缀如写的是 sass, 则最后返回的是 ROLE_sass。源码如下所示所以在UserDetailsService实现的接口中需要添加ROLE_前缀方法2使用 hasAnyRole方法表示拥有其中一个角色就可以访问用英文逗号隔开总结角色与权限的区别权限是具体控制某个操作的如普通用户不能有查看和删除其他用户的权限。而角色是一个身份标识在于这个身份有没有某个操作权限如管理员可以有增删查改用户的权限普通用户没有增删查改用户的权限。注意权限Authority- 细粒度具体操作hasAuthority(USER_DELETE) // 有删除用户权限hasAnyAuthority(USER_DELETE, ADMIN_DELETE) // 有任一删除权限角色Role- 粗粒度身份标识hasRole(ADMIN) // 是管理员角色hasAnyRole(ADMIN, MANAGER) // 是管理员或经理

相关新闻

2026年AI大模型技术解析与应用实践

2026年AI大模型技术解析与应用实践

1. 2026年AI大模型技术全景解析1.1 从文本生成到系统决策的范式革命2026年的AI大模型已经完成了从"会说话的鹦鹉"到"会思考的参谋"的质变。四年前,我们还在惊叹于GPT-3能写出流畅的散文;如今,大模型正在制药实验室设计分…

2026/7/27 6:15:17阅读更多 →
Web接口加密参数逆向实战:以某度翻译Acs-Token为例

Web接口加密参数逆向实战:以某度翻译Acs-Token为例

1. 项目概述:一次典型的Web端加密参数逆向之旅最近在分析一些网络应用的数据交互时,遇到了一个挺有意思的案例:某度翻译的Web端接口。和很多现代Web应用一样,它的请求里包含了一个关键的加密参数——Acs-Token。这个参数通常用于身…

2026/7/27 6:15:17阅读更多 →
BQ27Z846电池管理芯片:阈值恢复与立体温度保护机制深度解析

BQ27Z846电池管理芯片:阈值恢复与立体温度保护机制深度解析

1. 项目概述:深入BQ27Z846的“安全大脑”在锂离子电池的应用世界里,安全从来不是一句空话。无论是你口袋里的智能手机,还是路上飞驰的电动汽车,其内部电池包的安全运行,都依赖于一个沉默的“安全大脑”——电池管理系统…

2026/7/27 6:13:17阅读更多 →
AI大模型学习路径与Transformer架构实战指南

AI大模型学习路径与Transformer架构实战指南

1. AI大模型时代的学习路径规划去年当我第一次接触GPT-3时,就像打开了新世界的大门。作为一个在传统软件开发领域摸爬滚打十年的工程师,我深刻意识到:AI大模型正在重塑整个技术生态。但面对这个快速发展的领域,很多初学者往往不知…

2026/7/27 7:39:23阅读更多 →
第63章 「统一数学框架的萌芽」—— 悦儿篇

第63章 「统一数学框架的萌芽」—— 悦儿篇

夜色如墨,将未名湖笼罩在一片深沉的静谧里。数学科学中心大楼仅剩几扇窗户还亮着灯,其中最角落的那间办公室,灯光仿佛永远不会熄灭。悦儿站在几乎被写满的书写板前,白色板面上密密麻麻的公式与符号构成了一幅跨越数个数学疆域的思…

2026/7/27 7:39:23阅读更多 →
Arch Linux + Wine + i3wm:搭建《重武器老鼠》复古游戏环境

Arch Linux + Wine + i3wm:搭建《重武器老鼠》复古游戏环境

概述《重武器老鼠》(Mighty Rodent,也有民间称为重装老鼠)是一款 2006 年发布的街机风格 2D 射击游戏,原为 Windows 平台 RAR 自解压包。本文记录在 Arch Linux i3wm AMD 双显卡笔记本 上通过 Wine 完整运行该游戏的实战流程&am…

2026/7/27 7:39:23阅读更多 →
深入解析TI AM1705 ARM9 SoC:架构、外设与嵌入式开发实战

深入解析TI AM1705 ARM9 SoC:架构、外设与嵌入式开发实战

1. 项目概述与核心价值在嵌入式开发领域,选对一颗主控芯片往往意味着项目成功了一半。今天想和大家深入聊聊德州仪器(TI)的AM1705这颗ARM微处理器。它不是市场上最新、性能最强的芯片,但在工业控制、便携式数据终端、智能家居网关…

2026/7/27 7:39:23阅读更多 →
校园帮系统开题答辩全流程与实战技巧

校园帮系统开题答辩全流程与实战技巧

1. 项目概述:校园帮系统开题答辩全流程解析校园帮系统作为面向高校师生的综合性服务平台,其开题答辩是项目正式启动前的关键环节。我在参与多个校园信息化项目评审过程中发现,80%的团队在开题阶段都存在需求分析不清晰、技术路线论证不足等共…

2026/7/27 7:39:23阅读更多 →
基于TMS320LF2407A的数字PFC控制:从平均电流模式到定点DSP实现

基于TMS320LF2407A的数字PFC控制:从平均电流模式到定点DSP实现

1. 项目概述:当PFC遇上DSP,一场控制逻辑的“数字革命”在电源设计这个行当里干了十几年,我亲眼见证了控制技术从模拟到数字的深刻变迁。早期做功率因数校正(PFC),手边堆满了运放、比较器和一堆阻容元件&…

2026/7/27 7:37:22阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/27 1:14:34阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/27 1:14:52阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/27 1:14:56阅读更多 →
SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

1. 项目概述:从寄存器手册到实战指南 如果你手头有一份类似德州仪器(TI)TMS320x240xA系列DSP的SPI模块技术手册,看着里面密密麻麻的寄存器位定义、时序图和公式,是不是感觉头大?这份资料虽然权威&#xff0…

2026/7/27 0:00:24阅读更多 →
【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/27 0:00:24阅读更多 →
2007-2023年各市区县生态文明建设示范区DID

2007-2023年各市区县生态文明建设示范区DID

数据简介 自改革开放以来,我国依赖高投入、高资源消耗和高污染等传统发展模式实现了经济短期内的快速增长, 然而这也导致了严重的生态环境危机。因此,国家有力于推动企业高质量经济发展,协同生态保护的方针,从而从201…

2026/7/27 0:00:24阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/25 23:03:25阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/26 19:05:21阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/26 19:05:21阅读更多 →