Spring Security—Spring MVC 整合

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 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>

image.gif

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

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

    image.gif

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

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

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

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

      image.gif

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

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

        image.gif

        在xml中实现相同功能。

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

        image.gif

        无论是哪种配置,/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();
          }

          image.gif

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

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

          image.gif

          三、@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>

          image.gif

          一旦你正确配置了 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 ...
            }

            image.gif

            从 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 ...
              }

              image.gif

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

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

                image.gif

                然后我们可以通过使用 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 ...
                  }

                  image.gif

                  我们也可以在我们的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 database
                      attachedCustomUser.setFirstName(firstName);
                      // ...
                    }

                    image.gif

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

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

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

                      image.gif

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

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

                        image.gif

                        四、异步 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";
                            }
                          };
                          }

                          image.gif

                          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>

                          image.gif

                          前面的例子输出的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>
                          <!-- ... -->

                          image.gif

                          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;
                              }
                            }

                            image.gif

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



                            Doker 技术人的数码品牌!!!

                            文章下方有交流学习区!一起学习进步!也可以前往官网,加入官方微信交流群!!!

                            你的支持和鼓励是我创作的动力❗❗❗

                            官网:Doker 多克; 官方旗舰店首页-Doker 多克创新官方店-淘宝网 全品8.5折优惠,购前请和店小二说明来自《阿里云》!!!

                            目录
                            相关文章
                            |
                            27天前
                            |
                            JSON 安全 Java
                            什么是JWT?如何使用Spring Boot Security实现它?
                            什么是JWT?如何使用Spring Boot Security实现它?
                            118 5
                            |
                            11天前
                            |
                            设计模式 前端开发 Java
                            步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
                            通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
                            24 4
                            |
                            29天前
                            |
                            前端开发 Java 开发者
                            Spring MVC中的请求映射:@RequestMapping注解深度解析
                            在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
                            112 2
                            |
                            2月前
                            |
                            JSON 前端开发 Java
                            SSM:SpringMVC
                            本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
                            |
                            2月前
                            |
                            前端开发 Java 应用服务中间件
                            【Spring】Spring MVC的项目准备和连接建立
                            【Spring】Spring MVC的项目准备和连接建立
                            65 2
                            |
                            3月前
                            |
                            缓存 前端开发 Java
                            【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
                            Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
                            |
                            2月前
                            |
                            XML 前端开发 Java
                            Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
                            本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
                            206 0
                            Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
                            |
                            3月前
                            |
                            XML 缓存 前端开发
                            springMVC02,restful风格,请求转发和重定向
                            文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
                            springMVC02,restful风格,请求转发和重定向
                            |
                            4月前
                            |
                            Java 数据库连接 Spring
                            后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
                            文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
                            后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
                            |
                            4月前
                            |
                            XML JSON 数据库
                            SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
                            这篇文章详细介绍了RESTful的概念、实现方式,以及如何在SpringMVC中使用HiddenHttpMethodFilter来处理PUT和DELETE请求,并通过具体代码案例分析了RESTful的使用。
                            SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)