Spring Security的基本授权配置方式

news2025/4/17 23:18:38

参考:方法安全(Method Security) :: Spring Security Reference (springdoc.cn)、 授权 HttpServletRequest :: Spring Security Reference (springdoc.cn)

前文为:Spring Security:授权框架

一、HttpServletRequest授权

Spring Security 允许在 request 层(通过URL和请求类型等)建立授权模型。例如,可以让 /admin 下的所有页面都需要授权,而所有其他页面只需要认证。

1.1调度(Dispatche)授权

AuthorizationFilter 不仅在每个 request 上运行,而且在每个 dispatch 上运行。这意味着 REQUEST dispatch 需要授权,FORWARD、ERROR 和 INCLUDE 也需要:

  • 比如 Spring MVC 将一个请求 FORWARD 到一个渲染 Thymeleaf 模板的视图解析器,会发生两次授权:一次是授权 /endpoint,一次是转发到 Thymeleaf 以渲染 "endpoint" 模板。这就是 FORWARD dispatche。
  • 比如 Spring Boot 捕捉到一个异常后,会将它 dispatch 给 ERROR dispatch,也会发生两次授权:一次是授权 /endpoint ,一次是 dispatch error。

代码如

@Controller
public class MyController {
    @GetMapping("/endpoint")
    public String endpoint() {//Spring MVC的FORWARD dispatche
        return "endpoint";
    }
    @GetMapping("/endpoint2")
    public String endpoint2() {//Spring Boot的ERROR dispatch
        throw new UnsupportedOperationException("unsupported");
    }
}

进行放行,就需要:

http
    .authorizeHttpRequests((authorize) -> authorize
        .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
        .requestMatchers("/endpoint").permitAll()
        .requestMatchers("/endpoint2").permitAll()
        .anyRequest().denyAll()
    )

1.2Endpoint授权

对端点进行授权的方式,如/endpoint 只能被具有 USER 权限的终端用户访问:

@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers("/endpoint").hasAuthority('USER')
            .anyRequest().authenticated()
        )
        // ...

    return http.build();
}

观察代码可以发现,声明可以被分解为 pattern/rule 对,即requestMatchers("pattern").rule。

解释一下上述代码:

  • requestMatchers("/endpoint").XXX:授权规则
    • permitAll:该请求不需要授权。(不会从 session 中检索 Authentication)
    • denyAll:任何情况下都不允许。(不会从 session 中检索 Authentication)
    • hasAuthority: Authentication 有一个符合给定值的 GrantedAuthority。
    • hasRole:hasAuthority 的一个快捷方式,自动添加默认的前缀。
    • hasAnyAuthority:要求 Authentication 有一个符合任何给定值的 GrantedAuthority。
    • hasAnyRole:hasAnyAuthority 的一个快捷方式,自动添加默认的前缀。
    • access:该请求使用这个自定义的 AuthorizationManager 来确定访问权限。
  • requestMatchers(XXX):匹配请求
    • URI模式
      • Ant语言:如 requestMatchers("/resource/**").hasAuthority("USER"),代表——如果请求是 /resource 或一些子目录,则要求 USER 权限。
      • 正则表达式:如requestMatchers(RegexRequestMatcher.regexMatcher("/username/[A-Za-z0-9]+")).hasAuthority("USER"),代表包含 username 的路径和所有 username 必须是字母数字。
    • Http方法
      • 如:所有的 GET 都有 read 权限——requestMatchers(HttpMethod.GET).hasAuthority("read")。

也可以自定义RequestMatcher,然后提供给http:

RequestMatcher printview = (request) -> request.getParameter("print") != null;//自定义RequestMatcher
http
    .authorizeHttpRequests((authorize) -> authorize
        .requestMatchers(printview).hasAuthority("print")
        .anyRequest().authenticated()
    )

1.3案例

基础配置

如:配置 /auth/hello 的 get 请求需要 user 权限;配置 /auth/hello 的 post 请求需要 manager 权限;配置 /hello 的 get 请求需要 manager 权限:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(authorize -> authorize
                    .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
                    .requestMatchers(HttpMethod.GET,"/auth/hello").hasAuthority("user")
                    .requestMatchers(HttpMethod.POST,"/auth/hello").hasAuthority("manager")
                    .requestMatchers("/hello").hasAuthority("manager")
                    .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .formLogin(Customizer.withDefaults());
    http.userDetailsService(myJdbcDaoImpl());
    return http.build();
}

数据库中权限为:

自定义AuthorizationManager

复习一下AuthorizationManager是什么:用来判断授权是否通过的组件。

代码:

@Component
public final class OpenPolicyAgentAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
        return new AuthorizationDecision(true);
    }

    @Override
    public void verify(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
        AuthorizationManager.super.verify(authentication, object);
    }
}

@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> authz) throws Exception {
    http.authorizeHttpRequests((authorize) -> authorize
            .anyRequest().access(authz)
        );
    return http.build();
}

check内填写授权的方式,我直接返回授权成功了...RequestAuthorizationContext context 中包含了请求信息,可从中获得URL等信息进行判定。

1.4SpEL表示授权

Spring Security 将其所有的授权字段和方法都封装在一组根(root)对象中。最通用的根对象被称为 SecurityExpressionRoot,它是 WebSecurityExpressionRoot 的基础。当准备评估一个授权表达式时,Spring Security 将这个根对象提供给 StandardEvaluationContext。

常见方法:

  • permitAll - 该请求不需要授权即可调用;注意,在这种情况下,将不会从 session 中检索 Authentication。
  • denyAll - 该请求在任何情况下都是不允许的;注意在这种情况下,永远不会从会话中检索 Authentication。
  • hasAuthority - 请求要求 Authentication 的 GrantedAuthority 符合给定值。
  • hasRole - hasAuthority 的快捷方式,前缀为 ROLE_ 或任何配置为默认前缀的内容。
  • hasAnyAuthority - 请求要 Authentication 具有符合任何给定值的 GrantedAuthority。
  • hasAnyRole - hasAnyAuthority 的一个快捷方式,其前缀为 ROLE_ 或任何被配置为默认的前缀。
  • hasPermission - 用于对象级授权的 PermissionEvaluator 实例的 hook。

举例:

<http>
    <intercept-url pattern="/static/**" access="permitAll"/> 
    <intercept-url pattern="/admin/**" access="hasRole('ADMIN')"/> 
    <intercept-url pattern="/db/**" access="hasAuthority('db') and hasRole('ADMIN')"/> 
    <intercept-url pattern="/**" access="denyAll"/> 
</http>
  • 我们指定了一个任何用户都可以访问的 URL patter。具体来说,如果URL以 "/static/" 开头,任何用户都可以访问一个请求。
  • 任何以 "/admin/" 开头的 URL 将被限制给拥有 "ROLE_ADMIN" 角色的用户。你会注意到,由于我们调用的是 hasRole 方法,我们不需要指定 "ROLE_" 前缀。
  • 任何以 "/db/" 开头的 URL 都需要用户被授予 "db" 权限,并且是 "ROLE_ADMIN"。你会注意到,由于我们使用的是 hasRole 表达式,我们不需要指定 "ROLE_" 前缀。
  • 任何还没有被匹配的URL都会被拒绝访问。如果你不想意外地忘记更新你的授权规则,这是一个好的策略。

二、方法安全

2.1基础流程

除了对 HttpServletRequest 授权外,Spring Security 还支持在方法级别进行授权,方法安全具有细粒度、服务层的优点,并在风格上优于基于 HttpSecurity 的配置。

方法安全需要通过注解@EnableMethodSecurity启用,启用后即可使用注解 @PreAuthorize、@PostAuthorize、@PreFilter 和 @PostFilter。

代码举例:

@Service
public class MyCustomerService {
    @PreAuthorize("hasAuthority('permission:read')")
    @PostAuthorize("returnObject.owner == authentication.name")
    public Customer readCustomer(String id) { ... }
}

方法安全是基于Spring AOP实现的。调用流程:

  1. Spring AOP 为 readCustomer 调用了代理方法,调用了一个与 @PreAuthorize pointcut 匹配的拦截器 AuthorizationManagerBeforeMethodInterceptor。
  2. 拦截器调用 PreAuthorizeAuthorizationManager#check。
  3. PreAuthorizeAuthorizationManager 使用 MethodSecurityExpressionHandler 解析注解的 SpEL 表达式,并从包含 Supplier 和 MethodInvocation 的 MethodSecurityExpressionRoot 构建相应的 EvaluationContext。
  4. 拦截器使用该上下文来评估表达式;具体地说,它从 Supplier 读取 Authentication,并检查其 权限 集合中是否有 permission:read。
  5. 如果评估通过,Spring AOP 将继续调用该方法。
  6. 如果没有,拦截器会发布一个 AuthorizationDeniedEvent,并抛出一个 AccessDeniedException, ExceptionTranslationFilter 会捕获并向响应返回一个403状态码。
  7. 在方法返回后,Spring AOP 调用一个与 @PostAuthorize pointcut 相匹配的拦截器 AuthorizationManagerAfterMethodInterceptor,操作与上面相同,但使用了 PostAuthorizeAuthorizationManager。
  8. 如果评估通过(在这种情况下,返回值属于登录的用户),处理继续正常进行。
  9. 如果没有,拦截器会发布一个 AuthorizationDeniedEvent,并抛出一个 AccessDeniedException, ExceptionTranslationFilter 会捕获并向响应返回一个 403 状态码。

2.2注解使用介绍与举例

注解使用方式:

  1. 多个注解串联计算:如果方法调用涉及多个 方法安全注解,则每个注解一次处理一个(等同于&&)。
  2. 同一个方法上不能重复相同的注解。
  3. 注解内的授权使用 SpEL 定义。
  4. 在类和接口级别也支持方法安全注解,所有方法都继承类级别的行为。
  5. 通过@Param或@P注解,可在方法安全中使用方法参数。

@PreAuthorize应用代码举例:

@RestController
@RequestMapping("/auth")
public class TestController {

    @PreAuthorize("hasAuthority('user')")
    @GetMapping("/hello")
    public String sayHello(){
        return "hello security";
    }

    @PreAuthorize("hasAuthority('manager')")
    @PostMapping("/hello")
    public String sayHello2(){
        return "hello security";
    }
}

要求调用处理/auth/hello的get请求的控制权方法时需要user权限,调用处理/auth/hello的post请求的控制权方法时需要manager权限。

@PostAuthorize、@PreFilter、@PostFilter 应用举例:

@Component
public class BankService {//返回的 Account 对象.owner == authentication.name 通过时才能返回值。
    @PostAuthorize("returnObject.owner == authentication.name")
    public Account readAccount(Long id) {
        // ... is only returned if the `Account` belongs to the logged in user
    }
}
@Component
public class BankService {//过滤掉实参 accounts 中的 filterObject.owner == authentication.name 表达式失败的值。
//filterObject 代表 accounts 中的每个 account,用于测试每个 account。
    @PreFilter("filterObject.owner == authentication.name")
    public Collection<Account> updateAccounts(Account... accounts) {
        // ... `accounts` will only contain the accounts owned by the logged-in user
        return updated;
    }
}
@Component
public class BankService {//过滤掉返回值 accounts 中的 filterObject.owner == authentication.name 表达式失败的值。
    @PostFilter("filterObject.owner == authentication.name")
    public Collection<Account> readAccounts(String... ids) {
        // ... the return value will be filtered to only contain the accounts owned by the logged-in user
        return accounts;
    }
}

在方法安全中使用方法参数:

  1. @Param 注解:适用于方法有一个及以上参数使用注解。
    @PreAuthorize("#n == authentication.name")
    Contact findContactByName(@Param("n") String name);//要求 name 等于 Authentication#getName 才能对调用进行授权。
  2. @P 注解:适用于方法仅有一个参数使用注解。
    @PreAuthorize("hasPermission(#c, 'write')")
    public void updateContact(@P("c") Contact contact);//当前 Authentication 具有专门针对此 Contact 实例的 write 权限。

在方法安全中,注解的拦截器可以自定义,拦截器使用的AuthenticationManager也可以自定义;SpEL 中可以使用自定义 Bean 进行判断,SpEL的表达式的处理方式也可以自定义,这些内容请自行了解(方法安全(Method Security) :: Spring Security Reference (springdoc.cn))。 

2.3SpEL表达式案例

@Component
public class MyService {
    //任何人不得以任何理由调用该方法。
    @PreAuthorize("denyAll")
    MyResource myDeprecatedMethod(...);
    //该方法只能由被授予 ROLE_ADMIN 权限的 Authentication 调用。
    @PreAuthorize("hasRole('ADMIN')")
    MyResource writeResource(...)
    //该方法只能由被授予 db 和 ROLE_ADMIN 权限的 Authentication 调用。
    @PreAuthorize("hasAuthority('db') and hasRole('ADMIN')")
    MyResource deleteResource(...)
    //本方法仅可由 aud claim 等于 "my-audience" 的 Princpal 调用。
    @PreAuthorize("principal.claims['aud'] == 'my-audience'")
    MyResource readResource(...);
    //只有当 Bean authz 的 decide 方法返回 true 时,才能调用该方法。
    @PreAuthorize("@authz.decide(#root)")
    MyResource shareResource(...);
}
//Bean authz 的定义
@Component("authz")
public class AuthorizationLogic {
    public boolean decide(MethodSecurityExpressionOperations operations) {
        // ... authorization logic
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1156369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

xcode 安装及运行个人app编程应用

1.xcode 介绍 Xcode 是运行在操作系统Mac OS X上的集成开发工具&#xff08;IDE&#xff09;&#xff0c;由Apple Inc开发。Xcode是开发 macOS 和 iOS 应用程序的最快捷的方式。Xcode 具有统一的用户界面设计&#xff0c;编码、测试、调试都在一个简单的窗口内完成 2.xcode 下…

Redis统计大法:挖掘数据的四重宝藏【redis第五部分】

Redis统计大法&#xff1a;挖掘数据的四重宝藏 前言第一&#xff1a;redis集合统计简介第二&#xff1a;聚合统计->数据的综合分析总和&#xff08;Sum&#xff09;&#xff1a;平均值&#xff08;Average&#xff09;中位数&#xff08;Median&#xff09; 第三&#xff1a…

urlPattern配置和request

urlPattern配置 Servlet类编写好后&#xff0c;想要被访问到&#xff0c;就需要配置其访问路径&#xff08;urlPattern&#xff09; 一个Servlet可以配置多个urlPattern&#xff1a; WebServlet&#xff08;urlPatterns{"/demo","/demo2"}) urlPattern配置…

html获取网络数据,列表展示 一

html获取网络数据&#xff0c;列表展示 js遍历json数组中的json对象 image.png || - 判断数据是否为空&#xff0c;为空就显示 - <!DOCTYPE html> <html><head><meta charset"utf-8"><title>网页列表</title></head><b…

FreeRTOS深入教程(空闲任务和Tick中断深入分析)

文章目录 前言一、空闲任务源码分析二、Tick中断深入分析总结 前言 本篇文章主要带大家深入分析空闲任务和Tick中断的作用。 一、空闲任务源码分析 在启动调度器时会创建出空闲任务&#xff1a; /* 启动调度器 */ vTaskStartScheduler();在空闲任务中会调用到prvCheckTasks…

k8s pod获取ip地址过程

在学习 Kubernetes 网络模型的过程中&#xff0c;了解各种网络组件的作用以及如何交互非常重要。本文就介绍了各种网络组件在 Kubernetes 集群中是如何交互的&#xff0c;以及如何帮助每个 Pod 都能获取 IP 地址。 Kubernetes 网络模型的核心要求之一是每个 Pod 都拥有自己的 …

oracle 重启步骤及踩坑经验

oracle 重启步骤及踩坑经验 标准重启步骤 切换到oracle用户 su - oracle关闭监听 lsnrctl stop杀掉oracle有关进程 ps -ef|grep $ORACLE_SID|grep -v ora_|grep LOCALNO|awk {print $2}|xargs kill -9#查询pid ps -ef|grep $ORACLE_SID|grep -v ora_|grep LOCALNO|awk {p…

Linux--jdk、tomcat、环境配置,mysql安装、后端项目搭建

前言 上期我们讲到了安装linux虚拟机&#xff0c;这期我们来讲一下如何使用xshell和xftp在linux系统上搭建我们的单体项目 一、软件的传输 1.1 xftp Xftp是一款功能强大的文件传输软件&#xff0c;用于在本地主机和远程服务器之间进行快速、安全的文件传输。它是由南京帆软科…

【MongoDB】Windows 安装MongoDB 6.0

一、下载安装包 安装包下载地址https://www.mongodb.com/try/download/community这里我选择的是 二、解压并安装 1、解压 这里我将压缩包解压到了D盘&#xff0c;并重命名成了mongodb&#xff0c;解压后的目录如下&#xff1a; 2、创建配置文件 在D:\mongodb下新建conf目录…

数据库面试题整理

目录 MySQL事务隔离级别有哪几种&#xff1f;MySQL的常用的存储引擎有哪些&#xff1f;特点是什么&#xff0c;分别适合什么场景下使用MySQL有数据缓存吗&#xff1f;原理是怎么样的&#xff1f;InnoDB的缓冲池默认是开启的吗&#xff1f;基本原理是什么&#xff1f;会有脏数据…

Flutter 01 目录结构入门

一、Flutter目录结构&#xff1a; 二、Flutter入口文件、入口方法&#xff1a; 三、Flutter Demo&#xff1a; demo1&#xff1a; import package:flutter/material.dart;//MaterialApp 和 Scaffold两个组件装饰App void main() {runApp(MaterialApp(home: Scaffold(appBar: A…

当函数参数为一级指针,二级指针

当函数参数为一级指针&#xff0c;二级指针 在讲述内容之前&#xff0c;先讲四点重要知识 1.当传入参数时&#xff0c;函数形参会立即申请形参的内存空间&#xff0c;函数执行完毕后&#xff0c;形参的内存空间立即释放掉。 1.指针是存放其他变量地址的变量。指针有自己的内…

腾讯云轻量应用镜像、系统镜像、Docker基础镜像、自定义镜像和共享镜像介绍

腾讯云轻量应用服务器镜像类型分为应用镜像、系统镜像、Docker基础镜像、自定义镜像和共享镜像&#xff0c;腾讯云百科txybk.com来详细说下不同镜像类型说明和详细介绍&#xff1a; 轻量应用服务器镜像类型说明 腾讯云轻量应用服务器 应用镜像&#xff1a;独有的应用镜像除了包…

[threejs]让导入的gltf模型显示边框

边框1效果图如下&#xff1a; 代码如下&#xff1a; const gltfLoader1 new GLTFLoader();gltfLoader1.load( "/assets/box/1/scene.gltf" ,function(gltf){let model gltf.scene;model.scale.set(3,3,3)// scene1.add(model);// renderer1.render(scene1, camera…

SaaS 出海,如何搭建国际化服务体系?(一)

防噎指南&#xff1a;这可能是你看到的干货含量最高的 SaaS 出海经验分享&#xff0c;请准备好水杯&#xff0c;放肆食用&#xff08;XD。 当越来越多中国 SaaS 企业选择开启「国际化」副本&#xff0c;出海便俨然成为国内 SaaS 的新角斗场。 LigaAI 观察到&#xff0c;出海浪…

CSS3表格和表单样式

在传统网页中&#xff0c;表格主要用于网页布局&#xff0c;因此也成为网页编辑的主要工具&#xff1b;在标准化网页设计中&#xff0c;表格的主要功能是显示数据&#xff0c;也可适当辅助结构设计。本章主要介绍如何使用CSS控制表格和表单的显示效果&#xff0c;如表格和表单的…

在ffmpeg中,如何把h264转换为rgb格式

在ffmpeg中&#xff0c;网络视频流h264为什么默认的转为YUV而不是其他格式 文章中介绍了&#xff0c;h264解码的时候是直接解码为yuv的&#xff0c;如果在使用的过程中 需要用到rgb的格式&#xff0c;我们该如何来转换这种格式呢&#xff1f; 在上面的文章中&#xff0c;我们已…

基于YOLOv8模型暗夜下人脸目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型暗夜下人脸目标检测系统可用于日常生活中检测与定位黑夜下人脸目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法…

塞尔帕替尼的靶点以及疗效【医游记】

&#xff08;图片来源于网络&#xff09; 塞尔帕替尼&#xff08;Selpercatinib&#xff09;是一种高选择性和抑制活性的小分子RET&#xff08;受体酪氨酸激酶&#xff09;抑制剂。它是全球首个获批的高选择性RET抑制剂&#xff0c;用于治疗RET融合阳性的转移性非小细胞肺癌的…

GNSS卫星姿态解算

GNSS卫星姿态影响太阳光压辐射力的建模与卫星天线改正&#xff0c;正确解算卫星姿态是GNSS数据解算中的关键步骤。 卫星的姿态指卫星的星体坐标轴XYZ在惯性系下的指向&#xff0c;其中Z轴始终指向地球地心&#xff0c;Y轴为卫星太阳能帆板的旋转轴&#xff0c;它始终与太阳-卫星…