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

SpringSecurity 详细介绍(认证和授权)

1. 简介

  1. SpringSecurity 是一个强大且高度可定制的认证授权框架

    认证:身份验证,判断访问者是否为系统用户,某些操作只有系统用户才能访问

    授权:访问控制,判断系统用户是否具备访问某个功能的权限

  2. 依赖导入

<!--spring security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. 原理

Spring Security 的核心实现是一条过滤器链!Spring Security的所有功能、特性都建立在这条过滤器链上!

2.1 通过Debug查看过滤器链:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.2 核心过滤器:

  • UsernamePasswordAuthenticationFilter: 认证核心。

  • ExceptionTranslationFilter: 处理认证和授权异常。

  • FilterSecurityInterceptor: 授权核心。

3. 认证

3.1 核心认证流程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.2 核心接口

  • Authentication:身份认证令牌,封装了用户相关信息

  • AuthenticationManager:认证管理对象,用于对 Authentication 对象进行认证

  • UserDetailsService:用户信息查询服务,定义了 loadUserByUsername 方法查询用户信息

  • UserDetails:封装了用户的详细信息,UserDetailsService 查询的用户信息都会封装到这个对象中

3.3 问题一

3.3.1 问题介绍

UserDetailsService 在查询用户信息时,是从内存中查询的,而实际上,用户信息是存储在数据库中的,所以我们需要修改为从数据库中查询用户信息

3.3.2 解决方案

自定义 UserDetailsService 接口的实现类,从数据库中查询用户相关信息。

3.3.3 执行步骤

1. 自定义 UserDetailsService 接口的实现类

2. 创建 UserDetails 的实现类,用于分装 User 对象

3. 在输入正确的用户名和密码的情况下,登录失败了,控制台报如下错误

问题简介

原因

  • 如果未注入 PasswordEncoder 接口的加密实现,则数据库中的密码格式必须为:{id}encodedPassword

解释

  • id:表示加密方式,可以是 noop(不加密)、bcrypt、MD5、SHA-1、SHA-256等

  • encodedPassword:表示使用id指定的方式进行加密后的结果

SpringSecurity 会根据 id 指定的加密规则对密码进行验证,如果不指定加密方式,就会出现上面的错误!

修改数据库密码格式是可以实现的,但是不推荐,我们使用注入 PasswordEncoder 的方式!!!

解决方案

将 PasswordEncoder 的具体实现注入到Spring容器中,注册时密码的加密也要使用 PasswordEncoder

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4. 使用自定的加密方式

使用上面现成的 BCryptPasswordEncoder 加密方式已经很安全了,但是如果有特定的需求也可以自己定义

  • 自定义加密类
public class MyPasswordEncoder implements PasswordEncoder {public String encode(CharSequence rawPassword) {//这里填写自己自定义加密方式if (StringUtils.hasText(rawPassword))return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes(StandardCharsets.UTF_8));return null;}//自定义密码匹配规则(就是讲传来的密码加密后比对即可)public boolean matches(CharSequence rawPassword, String encodedPassword) {return encode(rawPassword).equals(encodedPassword);}
}
  • 在配置类中,将自定义的加密类注入容器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {//return NoOpPasswordEncoder.getInstance(); // 不加密处理,不推荐!//return new BCryptPasswordEncoder(); // 推荐!return new MyPasswordEncoder(); // 自定义加密处理}
}

3.4 问题二

3.4.1 问题描述

之所有使用 SpringScurity 登录一次后下一次不需要在登录了,他主要是基于 Session 实现的这个功能的,但是在前后端分离的情况下,前后端项目分别部署在不同的服务器上,导致 session 无法正常使用。

需要使用 **token(令牌)**认证:在用户登录成功之后,服务器需要为客户端创建一个 token,并将这个 token 作为 key 保存用户信息到 redis,响应时将这个 token 连同其它信息一并返回给客户端,客户端需要将这个 token 保存来,之后的请求都要将这个 token 发送给服务器,服务器通过这个 token 从 redis 中获取用户信息。

3.4.2 解决方案

  1. 自定义登录接口,将用户名和密码封装成 Authentication 对象,调用 AuthenticationManager的 authenticate 方法进行认证,认证通过后会返回 Authentication用户,生成 token 并将返回的Authentication对象存入redis,将token返回给客户端

  2. 自定义认证过滤器,根据请求中的token从redis中获取身份认证令牌Authentication对象,并调用SecurityContextHolder.getContext().setAuthentication方法将Authentication对象保存下来

  3. 将自定义认证过滤器注入到Spring容器中,并让这个过滤器在UsernamePasswordAuthenticationFilter之前执行

3.4.3 执行步骤

1. 修改 SpringSecurity 的配置类中的配置

  1. 将认证管理对象 AuthenticationManager 注入到Spring容器中,我们自定义登录接口时要用到
  2. 对请求进行认证配置:访问登录接口时,不需要认证
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {// return NoOpPasswordEncoder.getInstance(); // 不加密处理,不推荐!return new BCryptPasswordEncoder(); // 强烈推荐!}//向Spring 注入 AuthenticationManager 对象@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}//这里可以指定你自定义的登录路径,无需经过认证@Overrideprotected void configure(HttpSecurity http) throws Exception {// 前后端分离固定配置separationModel(http);// 对请求进行认证配置http.authorizeRequests()// (匿名访问)无需认证也可以访问 anonymous:匿名.antMatchers("/user/login").anonymous()// 其它请求认证通过后可以访问.anyRequest().authenticated();}//前后端分离固定配置private void separationModel(HttpSecurity http) throws Exception {// 禁用csrfhttp.csrf().disable();// 允许跨域http.cors();// 不使用sessionhttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);}
}

2. 自定义登录接口

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.4.4. 编写认证过滤器

主要实现判断用户是否已经登录,如果已经登录,则无需再登录

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.4.5 设置自定义过滤器放过滤器链的位置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4. 授权

4.1 简介

实际场景应用

  • 一个系统中有很多功能,而某些功能需要一定的权限才能使用,例如商城中的商品,普通用户只能查看和购买,但不具备删除、修改的权限,这时就需要权限控制,也就是所谓的授权。

  • 但是要注意的是这里并不是简单的将前端页面跟该功能相关的按钮抹去(此时可以通过路径也可以执行该功能),还要对访问接口进行权限限制。

4.2 快速使用

这里用户的权限不通过数据库的方式去获取,而是通过写死的方式,这样便于演示。

1. 首先需要在 SpringSecurity 的配置类上添加下面注解,来开启权限控制的注解开发

@EnableGlobalMethodSecurity(prePostEnabled = true)

2. 然后在 Controller 的方法上使用 @PreAuthorize 注解进行权限控制

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

登录成功之后,“/test” 接口可以正常访问,而"/delete"接口访问失败,因为不具备”delete“权限

用户的权限信息,需要在认证成功之后查询,连同用户信息一起封装到UserDetails中

3. 为对象添加权限(写死的方式)

  • 修改 UserDetail 字段

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 为 Userdetail 的权限列表赋值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.3 通过数据库获取权限信息

4.3.1 RBAC模型(数据库设计模型)

RBAC(Role-Based Access Control)权限模型:基于角色的权限控制,是一种比较通用的权限模型,最常用!通俗一点就是在用户与权限之间加入一个角色,以角色为桥梁获取用户所具有的权限。

4.3.2 编写 Mapper

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.3.3 业务实现

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.4 其他权限控制注解

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.5 自定义权限校验方法

用的不多,了解即可

  1. 定义用于校验权限的类

    @Component("ex")
    public class MyExpressionRoot {public boolean hasAuthority(String authority){//获取身份令牌Authentication authentication = SecurityContextHolder.getContext().getAuthentication();//获取权限Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//循环判断for (GrantedAuthority grantedAuthority : authorities) {String role = grantedAuthority.getAuthority();if (role.equals(authority)) {return true;}}return false;}
    }
    
  2. 在@PreAuthorize注解中使用自定义校验方法

    @DeleteMapping("delete")
    //ex是校验实力在Spring容器中的唯一标识
    @PreAuthorize("@ex.hasAuthority('delete')")
    public String delete() {return "删除成功!";
    }
    
http://www.xdnf.cn/news/1162297.html

相关文章:

  • 复制docker根目录遇到的权限问题
  • C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(二)
  • docker磁盘空间不足解决办法
  • MongoDB 查询时区问题
  • linux定时器使用
  • 3、Spring AI_DeepSeek模型-多轮对话
  • 江苏思必驰科技25Java实习面经
  • HTTP,HTTPS
  • 服务器系统时间不准确怎么办?
  • 图论基本算法
  • 部署Zabbix企业级分布式监控
  • 【Unity基础】Unity中2D和3D项目开发流程对比
  • Unity 插件Resize Pro 最快的 Texture2D 调整大小工具
  • Elasticsearch 是 NVIDIA Enterprise AI Factory 验证设计中推荐的向量数据库
  • 数据结构堆的实现(C语言)
  • Web3.0 能为你带来哪些实质性的 改变与突破
  • Vue 脚手架——render函数
  • 【算法笔记】树状数组
  • Linux学习之Linux系统权限
  • 《C++》函数内联,auto关键字
  • 用基础模型构建应用(第十章)AI Engineering: Building Applications with Foundation Models学习笔记
  • 探索无广告音乐世界:MusicFree 免费播放器
  • 海康威视视觉算法岗位30问及详解
  • BERT 的“池化策略”
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 主页布局实现
  • Three.js 立方体贴图(CubeMap)完全指南:从加载到应用
  • 大模型高效适配:软提示调优 Prompt Tuning
  • Python高效入门指南
  • 深入详解随机森林在放射治疗计划优化中的应用及实现细节
  • 部署 Zabbix 企业级分布式监控