Spring Security—Spring MVC 整合

目录

一、@EnableWebMvcSecurity

二、MvcRequestMatcher

三、@AuthenticationPrincipal

四、异步 Spring MVC 整合

五、Spring MVC 和 CSRF 整合

1、自动包含 Token

2、解析 CsrfToken


Spring Security提供了一些与Spring MVC的可选整合。本节将进一步详细介绍这种整合。

一、@EnableWebMvcSecurity

从Spring Security 4.0开始,@EnableWebMvcSecurity 已被弃用。取而代之的是 @EnableWebSecurity,它根据classpath增加了Spring MVC功能。

要启用Spring Security与Spring MVC的整合,请在配置中添加 @EnableWebSecurity 注解。

Spring Security通过使用Spring MVC的 WebMvcConfigurer 提供配置。这意味着,如果你使用更高级的选项,如直接与 WebMvcConfigurationSupport 集成,你需要手动提供Spring Security的配置。

二、MvcRequestMatcher

Spring Security提供了与Spring MVC如何通过 MvcRequestMatcher 匹配URL的深度集成。这有助于确保你的安全规则与用于处理请求的逻辑相匹配。

要使用 MvcRequestMatcher,你必须将Spring Security配置放在与 DispatcherServlet 相同的 ApplicationContext 中。这是必要的,因为Spring Security的 MvcRequestMatcher 期望一个名称为 mvcHandlerMappingIntrospector 的 HandlerMappingIntrospector Bean被你的Spring MVC配置注册,用于执行匹配。

对于 web.xml 文件,这意味着你应该把你的配置放在 DispatcherServlet.xml 中。

<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener><!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ -->
<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/*.xml</param-value>
</context-param><servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- Load from the ContextLoaderListener --><init-param><param-name>contextConfigLocation</param-name><param-value></param-value></init-param>
</servlet><servlet-mapping><servlet-name>spring</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>

以下 WebSecurityConfiguration 被放置在 DispatcherServlet 的 ApplicationContext 中。

  • Java
public class SecurityInitializer extendsAbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return null;}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[] { RootConfiguration.class,WebMvcConfiguration.class };}@Overrideprotected String[] getServletMappings() {return new String[] { "/" };}
}

我们总是建议你通过对 HttpServletRequest 和安全方法(method security)进行匹配来提供授权规则。

通过对 HttpServletRequest 的匹配来提供授权规则是很好的,因为它发生在代码路径的早期,有助于减少 attack surface(攻击面)。安全方法确保,如果有人绕过了网络授权规则,你的应用程序仍然是安全的。这就是所谓的 Defense in Depth(深度防御)

考虑一个Controller,它有如下mapping。

  • Java
@RequestMapping("/admin")
public String admin() {// ...
}

为了限制管理员用户对这个Controller方法的访问,你可以通过在 HttpServletRequest 上匹配以下内容来提供授权规则。

  • Java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.requestMatchers("/admin").hasRole("ADMIN"));return http.build();
}

在xml中实现相同功能。

<http><intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
</http>

无论是哪种配置,/admin URL都要求被认证的用户是一个管理员用户。然而,根据我们的Spring MVC配置,/admin.html URL也映射到我们的 admin() 方法。此外,根据我们的Spring MVC配置,/admin URL也映射到我们的 admin() 方法。

问题是,我们的安全规则只保护 /admin。我们可以为Spring MVC的所有排列组合添加额外的规则,但这将是相当冗长和乏味的。

幸运的是,当使用 requestMatchers DSL方法时,如果Spring Security检测到Spring MVC在classpath中可用,它会自动创建一个 MvcRequestMatcher。因此,它将通过使用Spring MVC对URL进行匹配来保护Spring MVC的相同URL。

在使用 Spring MVC 时,一个常见的要求是指定servlet路径属性,为此你可以使用 MvcRequestMatcher.Builder 来创建多个共享相同servlet路径的 MvcRequestMatcher 实例。

  • Java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");http.authorizeHttpRequests((authorize) -> authorize.requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN").requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER"));return http.build();
}

下面的XML具有相同的效果。

<http request-matcher="mvc"><intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
</http>

三、@AuthenticationPrincipal

Spring Security提供了 AuthenticationPrincipalArgumentResolver,它可以自动解析Spring MVC参数的当前 Authentication.getPrincipal()。通过使用 @EnableWebSecurity,你会自动将其添加到你的Spring MVC配置中。如果你使用基于XML的配置,你必须自己添加这个。

<mvc:annotation-driven><mvc:argument-resolvers><bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" /></mvc:argument-resolvers>
</mvc:annotation-driven>

一旦你正确配置了 AuthenticationPrincipalArgumentResolver,你就可以在Spring MVC层中完全与Spring Security脱钩。

考虑这样一种情况:一个自定义的 UserDetailsService 返回一个实现 UserDetails 的 Object 和你自己的 CustomUser Object。当前认证的用户的 CustomUser 可以通过使用以下代码来访问。

  • Java
  • Kotlin
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser() {Authentication authentication =SecurityContextHolder.getContext().getAuthentication();CustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal();// .. find messages for this user and return them ...
}

从 Spring Security 3.2 开始,我们可以通过添加一个注解来更直接地解决这个争论。

  • Java
import org.springframework.security.core.annotation.AuthenticationPrincipal;// ...@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {// .. find messages for this user and return them ...
}

有时,你可能需要以某种方式转变 principal。例如,如果 CustomUser 需要是 final 的,它就不能被继承。在这种情况下,UserDetailsService 可能会返回一个实现 UserDetails 的 Object,并提供一个名为 getCustomUser 的方法来访问 CustomUser。

  • Java
public class CustomUserUserDetails extends User {// ...public CustomUser getCustomUser() {return customUser;}
}

然后我们可以通过使用 SpEL 表达式 访问 CustomUser,该表达式使用 Authentication.getPrincipal() 作为根对象。

  • Java
import org.springframework.security.core.annotation.AuthenticationPrincipal;// ...@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = "customUser") CustomUser customUser) {// .. find messages for this user and return them ...
}

我们也可以在我们的SpEL表达式中引用Bean。例如,如果我们使用JPA来管理我们的用户,如果我们想修改和保存当前用户的一个属性,我们可以使用下面的方法。

  • Java
import org.springframework.security.core.annotation.AuthenticationPrincipal;// ...@PutMapping("/users/self")
public ModelAndView updateName(@AuthenticationPrincipal(expression = "@jpaEntityManager.merge(#this)") CustomUser attachedCustomUser,@RequestParam String firstName) {// change the firstName on an attached instance which will be persisted to the databaseattachedCustomUser.setFirstName(firstName);// ...
}

我们可以通过让 @AuthenticationPrincipal 成为我们自己注解的元注解来进一步消除对 Spring Security 的依赖。下一个例子演示了我们如何在一个名为 @CurrentUser 的注解上这样做。

为了消除对Spring Security的依赖,消费应用程序将创建 @CurrentUser。这一步不是严格要求的,但有助于将你对Spring Security的依赖性隔离到一个更集中的位置。

  • Java
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}

我们已经将对Spring Security的依赖性隔离到一个文件中。现在 @CurrentUser 已经被指定,我们可以用它来发出信号(signal)来解决我们的当前认证用户的 CustomUser。

  • Java
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) {// .. find messages for this user and return them ...
}

四、异步 Spring MVC 整合

Spring Web MVC 3.2+对 异步请求处理有很好的支持。不需要额外的配置,Spring Security会自动将 SecurityContext 设置为调用 Controller 返回的 Callable 的 Thread。例如,下面的方法会自动用创建 Callable 时可用的 SecurityContext 来调用它的 Callable。

  • Java
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {return new Callable<String>() {public Object call() throws Exception {// ...return "someView";}
};
}

Associating SecurityContext to Callable’s

将 SecurityContext 与 Callable 关联起来从技术上讲,Spring Security与 WebAsyncManager 集成。用于处理 Callable 的 SecurityContext 是 startCallableProcessing 被调用时存在于 SecurityContextHolder 上的 SecurityContext。

没有与 Controller 返回的 DeferredResult 的自动整合。这是因为 DeferredResult 是由用户处理的,因此,没有办法与它自动整合。然而,你仍然可以使用 并发支持来提供与Spring Security的透明整合。

五、Spring MVC 和 CSRF 整合

Spring Security与Spring MVC集成,增加CSRF保护。

1、自动包含 Token

Spring Security 在使用 Spring MVC表单标签 的表单中自动 [包含CSRF Token。考虑一下下面的JSP。

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"xmlns:c="http://java.sun.com/jsp/jstl/core"xmlns:form="http://www.springframework.org/tags/form" version="2.0"><jsp:directive.page language="java" contentType="text/html" />
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><!-- ... --><c:url var="logoutUrl" value="/logout"/><form:form action="${logoutUrl}"method="post"><input type="submit"value="Log out" /><input type="hidden"name="${_csrf.parameterName}"value="${_csrf.token}"/></form:form><!-- ... -->
</html>
</jsp:root>

前面的例子输出的HTML与下面类似。

<!-- ... --><form action="/context/logout" method="post">
<input type="submit" value="Log out"/>
<input type="hidden" name="_csrf" value="f81d4fae-7dec-11d0-a765-00a0c91e6bf6"/>
</form><!-- ... -->

2、解析 CsrfToken

Spring Security提供了 CsrfTokenArgumentResolver,它可以自动解析Spring MVC参数的当前 CsrfToken。通过使用 @EnableWebSecurity,你会自动将其添加到你的Spring MVC配置中。如果你使用基于XML的配置,你必须自己添加这个。

一旦 CsrfTokenArgumentResolver 被正确配置,你就可以将 CsrfToken 暴露给你基于HTML的静态应用程序。

  • Java
@RestController
public class CsrfController {@RequestMapping("/csrf")public CsrfToken csrf(CsrfToken token) {return token;}
}

对其他域保持 CsrfToken 的 secret 是很重要的。这意味着,如果你使用 跨源资源共享(CORS),你不应该将 CsrfToken 暴露给任何外部域。

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

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

相关文章

UML中类之间的六种主要关系

UML中类之间的六种主要关系: 继承&#xff08;泛化&#xff09;&#xff08;Inheritance、Generalization&#xff09;, 实现&#xff08;Realization&#xff09;&#xff0c;关联&#xff08;Association)&#xff0c;聚合&#xff08;Aggregation&#xff09;&#xff0c;组…

Linux--进程替换

1.什么是进程替换 在fork函数之后&#xff0c;父子进程各自执行代码的一部分&#xff0c;但是如果子进程想要执行一份全新的程序呢&#xff1f; 通过进程替换来完成&#xff0c;进程替换就是父子进程代码发生写时拷贝&#xff0c;子进程执行自己的功能。 程序替换就是通过特定的…

python 笔记:h5py 读取HDF5文件

1 HDF5文件 HDF5 是 Hierarchical Data Format version 5 的缩写&#xff0c;是一种用于存储和管理大量数据的文件格式一个h5py文件可以看作是 “dataset” 和 “group” 二合一的容器 dataset : 数据集&#xff0c;像 numpy 数组一样工作group : 包含了其它 dataset 和 其它 …

GZ035 5G组网与运维赛题第4套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项&#xff08;高职组&#xff09; 赛题第4套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通&#xff08;35分&#xff09; 子任务1&#xff1a;5G公共网络部署与调试&#xff08;15分&#xff09; 子…

C语言_断言assert详解

一、assert定义 assert() 的用法像是一种"契约式编程"&#xff0c;在我的理解中&#xff0c;其表达的意思就是&#xff0c;程序在我的假设条件下&#xff0c;能够正常良好的运作&#xff0c;其实就相当于一个 if 语句&#xff1a; if(假设成立) {程序正常运行&…

(免费领源码) Asp.Net#SQL Server校园在线投票系统10557-计算机毕业设计项目选题推荐

摘 要 随着互联网大趋势的到来&#xff0c;社会的方方面面&#xff0c;各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去&#xff0c;而其中最好的方式就是建立网络管理系统&#xff0c;并对其进行信息管理。由于现在网络的发达&#xff0c;校园投票通过网…

java - IDEA IDE - 设置字符串断点

文章目录 java - IDEA IDE - 设置字符串断点概述笔记END java - IDEA IDE - 设置字符串断点 概述 IDE环境为IDEA2022.3 在看一段序列化的代码, 想找出报错抛异常那个点, 理解一下代码实现. 因为序列化代码实现在第三方jar包中, 改不了(只读的). 根本数不清第几次才会开始报…

java基础之泛型

泛型 泛型是在JDK1.5增加的功能&#xff0c;在没有泛型之前&#xff0c;从集合中取出来的每一个对象都必须进行强制类型转换&#xff0c;如果有人插入了错误类型的对象&#xff0c;在运行时的转换就会出现问题&#xff0c;有了泛型之后&#xff0c;这些问题就会在编译期暴露出来…

OpenCV学习(五)——图像基本操作(访问图像像素值、图像属性、感兴趣区域ROI和图像边框)

图像基本操作 5. 图像基本操作5.1 访问像素值并修改5.2 访问图像属性5.2 图像感兴趣区域ROI5.3 拆分和合并图像通道5.4 为图像设置边框&#xff08;填充&#xff09; 5. 图像基本操作 访问像素值并修改访问图像属性设置感兴趣区域&#xff08;ROI&#xff09;分割和合并图像 …

如何在vscode中添加less插件

Less &#xff08;Leaner Style Sheets 的缩写&#xff09; 是一门向后兼容的 CSS 扩展语言。它对CSS 语言增加了少许方便的扩展&#xff0c;通过less可以编写更少的代码实现更强大的样式。但less不是css&#xff0c;浏览器不能直接识别&#xff0c;即浏览器无法执行less代码&a…

2023年正版win10/win11系统安装教学(纯净版)

第一步&#xff1a;准备一个8G容量以上的U盘。 注意&#xff0c;在制作系统盘时会格式化U盘&#xff0c;所以最好准备个空U盘&#xff0c;防止资料丢失。 第二步&#xff1a;制作系统盘。 安装win10 进入windows官网 官网win10下载地址&#xff1a;https://www.microsoft.c…

安卓开发实例:随机数

点击按钮生成一个1-100之间的随机数 activity_random_number.xml <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android"http://schemas.android.com/apk/res/android"xmlns:a…

conda 复制系统环境

直接复制 想要通过 conda 直接复制一个已存在的环境&#xff0c;你可以使用 conda create 命令并配合 --clone 参数。以下是具体步骤&#xff1a; 查看现有的环境: 首先&#xff0c;你可以使用以下命令来查看所有的 conda 环境&#xff1a; conda env list这会给你一个环境列表…

TypeScript -类型断言的简单理解

类型断言是干啥的 类型断言 &#xff1a; 是手动的给某个变量 指定类型&#xff0c;从而可以方便操作该类型的属性和方法。 类型断言的两种写法 方式一 &#xff1a; 变量名 as 类型 let x: number | string abc; console.log((x as string).length); // 输出 3 &#xff0c;因…

10月Java行情 回暖?

最近面试 Java 开发&#xff0c;看看行情。总的来说没有前几年好&#xff0c;真的是互联网寒冬&#xff0c;面试机会都比以前少了不少&#xff0c;很多互联网公司都在降本增效&#xff0c;只招少量的人。如果你能约到面试就好好珍惜&#xff0c;记住不要裸辞&#xff0c;不要裸…

记一次vue3实现TRSP大华相机拉流的经历

一、背景 业务场景&#xff0c;大华IP相机安装在A城市某建筑场所&#xff0c;工控机是内网通过4G流量卡上网&#xff0c;工控机通过相机采集数据后做故障识别并上传故障信息到地面服务器&#xff0c;地面服务器在B城市。 现需要在地面服务器提供的WEB界面上实现IP相机实时拉流…

linux套接字选项API

获取套接字的选项值(getsockopt) 【头文件】 #include <sys/types.h> #include <sys/socket.h> 【函数原型】 int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen); 【函数功能】 用于获取一个套接字的选项 【参数含义】 […

P1025 [NOIP2001 提高组] 数的划分 题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 数据范围提示思路与部分实现完整代码 题目描述 将整数 n n n 分成 k k k 份&#xff0c;且每份不能为空&#xff0c;任意两个方案不相同&#xff08;不考虑顺序&#xff09;。 例如&#xff1a; n 7 n7 n7&#xf…

C++编译与运行:其一、静态类型和动态类型

一、什么是静态类型和动态类型&#xff1f; 先说结论&#xff1a;编译期间可以明确的类型是静态类型&#xff1b;运行期间才能明确的类型是动态类型。 后半句可能有点不好理解&#xff0c;通俗地说&#xff0c;需要通过执行代码才能明确的类型是动态类型。 假如我们有两个类&…

【前端】NodeJS核心知识点整理

1.Node.js入门案例 1.1.什么是Node.js JS是脚本语言&#xff0c;脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS&#xff0c;浏览器充当了解析器的角色。而对于需要独立运行的JS&#xff0c;NodeJS就是一个解析器。 每一种解析器都是一个运行环境&#xff0c;不但…