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 中。


  org.springframework.web.context.ContextLoaderListener




  contextConfigLocation
  /WEB-INF/spring/*.xml



  spring
  org.springframework.web.servlet.DispatcherServlet
  
  
    contextConfigLocation
    
  



  spring
  /

以下 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[] { "/" };
  }
}

我们总是建议你通过对 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中实现相同功能。


	

无论是哪种配置,/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具有相同的效果。


	

三、@AuthenticationPrincipal

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


		
				
		

一旦你正确配置了 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 database
	attachedCustomUser.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 processUpload(final MultipartFile file) {

return new Callable() {
	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。


	

	

	
	
	
	
	

	

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



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 暴露给任何外部域。

你可能感兴趣的:(java基础,spring,mvc,hive)