Spring 安全配置不适用于 Spring SimpleUrlHandlerMapping [英] Spring security configuration does not apply with Spring SimpleUrlHandlerMapping

查看:36
本文介绍了Spring 安全配置不适用于 Spring SimpleUrlHandlerMapping的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个 Spring Boot 应用程序,在其中我通过 SimpleUrlHandlerMapping 配置向 bean 注册了一个 URL.为什么我不使用 @Controller 或 @RequestMapping 类来执行此操作?!!因为我想在运行时动态注册 URL.

我正在使用以下代码向控制器注册一个简单的 URL

 @Bean公共 SimpleUrlHandlerMapping sampleServletMapping() {SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();mapping.setOrder(Integer.MAX_VALUE - 2);属性 urlProperties = new Properties();urlProperties.put("/index", "myController");mapping.setMappings(urlProperties);返回映射;}

上面的代码运行良好,我可以点击注册名为myController"的控制器 bean.

问题出现在我使用 spring security 时.我介绍了spring security,配置了InMemoryAuthentication,我的配置如下

 @Overrideprotected void configure(HttpSecurity http) 抛出异常 {http.authorizeRequests().antMatchers("/index").permitAll().anyRequest().permitAll();}

执行此操作后,当我尝试访问/index 路径时,它会抛出 403 禁止错误.我已经尝试过 permitAll() 和fullyAuthenticated() 配置.它似乎不起作用.但是,任何使用 @Controller 和 @RequestMapping 注释注册的 Controller 类都可以与 Security 完美配合.

因此,我的假设是 Spring Security 不知道通过 SimpleUrlHandlerMapping 动态注册的 URL.

我该如何解决这个问题?有没有办法告诉 spring security 包含我的动态 URL 注册?无法在网上找到任何关于此的文章.

非常感谢建议和帮助.

解决方案

更新:

为什么 csrf().disable() 有效

CSRF 代表跨站请求伪造

简单来说,它是一种与请求一起发送以防止攻击的令牌.为了使用 Spring Security CSRF 保护,我们首先需要确保我们使用正确的 HTTP 方法来修改状态(PATCHPOSTPUTDELETE - 而不是 GET).

使用 Spring CookieCsrfTokenRepository 的 CSRF 保护工作如下:

  1. 客户端向服务器(Spring Boot 后端)发出 GET 请求,例如请求主页面
  2. Spring 发送对 GET 请求的响应以及包含安全生成的 XSRF 令牌的 Set-cookie 标头
  3. 浏览器使用 XSRF Token 设置 cookie
  4. 在发送状态更改请求(例如 POST)时,客户端(可能是有角度的)将 cookie 值复制到 HTTP 请求标头
  5. 请求与标头和 cookie 一起发送(浏览器自动附加 cookie)
  6. Spring 比较 header 和 cookie 的值,如果相同则接受请求,否则返回 403 给客户端

方法 withHttpOnlyFalse 允许 angular 读取 XSRF cookie.确保 Angular 在 withCreddentials 标志设置为 true 的情况下发出 XHR 请求.

欲了解更多详情,您可以探索以下内容

更新方法configure(HttpSecurity http)

 http.csrf().ignoringAntMatchers("endpoint-to-be-ignored-for-csrf").csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and().authorizeRequests().antMatchers("/index").permitAll().anyRequest().authenticated();

<小时>

antMatcherspermitAll() 中指定的端点不需要身份验证,antMatchers("/index").permitAll() 应该可以工作很好.

  • 确保您的安全配置类使用 @EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true,secureEnabled = true) 进行注释
  • 安全配置类在遵循包结构并被Spring扫描.spring-component-scanning

您可以在此处 找到最小的工作示例><小时>

SecurityConfiguration.java

import org.springframework.context.annotation.Bean;导入 org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;导入 org.springframework.security.config.annotation.web.builders.HttpSecurity;导入 org.springframework.security.config.annotation.web.builders.WebSecurity;导入 org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;导入 org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;导入 org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;导入 org.springframework.security.crypto.password.PasswordEncoder;@启用网络安全@EnableGlobalMethodSecurity(prePostEnabled = true,secureEnabled = true)公共类 SecurityConfiguration 扩展了 WebSecurityConfigurerAdapter {@豆public PasswordEncoder passwordEncoder() {返回新的 BCryptPasswordEncoder();}//https://stackoverflow.com/a/56389047/10961238 ->网络安全与 HttpSecurity//如果 .antMatchers("/index").permitAll() 不起作用,请添加此方法@覆盖公共无效配置(WebSecurity web)抛出异常{网络调试(真);//网络//.ignoring()//.antMatchers("/index");}@覆盖公共无效配置(HttpSecurity http)抛出异常{http.csrf().disable().authorizeRequests().antMatchers("/index").permitAll()//注释这行会得到403.anyRequest().authenticated();}}

SampleController.java

import org.springframework.stereotype.Controller;导入 org.springframework.web.servlet.ModelAndView;导入 org.springframework.web.servlet.mvc.AbstractController;导入 javax.servlet.http.HttpServletRequest;导入 javax.servlet.http.HttpServletResponse;@Controller("myController")公共类 SampleController 扩展 AbstractController {@覆盖受保护的 ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 抛出异常 {System.out.println(":::::::::::::::::::::::::::::: Controller::::::::::::::::::::::::::::::::::");response.getWriter().print("Hello world!");返回空;}}

MainApplication.java

import com.example.mappings.controller.SampleController;导入 org.springframework.boot.SpringApplication;导入 org.springframework.boot.autoconfigure.SpringBootApplication;导入 org.springframework.context.annotation.Bean;导入 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;导入 java.util.Properties;@SpringBootApplication公共类映射应用{公共静态无效主(字符串 [] args){SpringApplication.run(MappingsApplication.class, args);}@豆公共 SimpleUrlHandlerMapping sampleServletMapping() {System.out.println("::::::::::::::::::::::::::::::::SimpleUrlHandlerMapping::::::::::::::::::::::::::::::::::");SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();mapping.setOrder(Integer.MAX_VALUE - 2);属性 urlProperties = new Properties();urlProperties.put("/index", sampleController());mapping.setMappings(urlProperties);返回映射;}@豆public SampleController sampleController() {System.out.println("::::::::::::::::::::::::::::::::Setting SampleController:::::::::::::::::::::::::::::::::");返回新的 SampleController();}}

application.properties

spring.security.user.name = 用户spring.security.user.password = 用户spring.security.user.roles = 管理员

I am writing a spring boot application in which I am registering a URL to a bean via the SimpleUrlHandlerMapping configuration. Why am I not using the @Controller or @RequestMapping classes to do this ?!! Because I want to dynamically register URL's during runtime.

I am using the following code to register a simple URL to a controller

    @Bean
    public SimpleUrlHandlerMapping sampleServletMapping() {
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(Integer.MAX_VALUE - 2);

        Properties urlProperties = new Properties();
        urlProperties.put("/index", "myController");

        mapping.setMappings(urlProperties);

        return mapping;
    }

The above code is working fine, I am able to hit the controller bean registered with the name "myController".

The issue appears when I use spring security. I introduced spring security and configured InMemoryAuthentication, and set my configuration as follows.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/index").permitAll()
                .anyRequest()
                .permitAll();
    }

After doing this when I try to access /index path, it throws a 403, forbidden error. I have tried with permitAll() and fullyAuthenticated() configurations. It doesn't seem to work. However, any Controller class registered with the @Controller and @RequestMapping annotations are perfectly working fine with Security.

So, my assumption is that Spring Security is not aware of the dynamically registered URL's via the SimpleUrlHandlerMapping.

How do I solve this ? Is there a way I can tell spring security to include my dynamic URL registrations ? Unable to find any article on this online.

Suggestions and help much appreciated.

解决方案

UPDATE:

Why csrf().disable() does works

CSRF stands for Cross Site Request Forgery

In simple words, it is one kind of token that is sent with the request to prevent the attacks. In order to use the Spring Security CSRF protection, we'll first need to make sure we use the proper HTTP methods for anything that modifies the state (PATCH, POST, PUT, and DELETE – not GET).

CSRF protection with Spring CookieCsrfTokenRepository works as follows:

  1. The client makes a GET request to Server (Spring Boot Backend), e.g. request for the main page
  2. Spring sends the response for GET request along with Set-cookie header which contains securely generated XSRF Token
  3. The browser sets the cookie with XSRF Token
  4. While sending a state-changing request (e.g. POST) the client (might be angular) copies the cookie value to the HTTP request header
  5. The request is sent with both header and cookie (browser attaches the cookie automatically)
  6. Spring compares the header and the cookie values, if they are the same the request is accepted, otherwise, 403 is returned to the client

The method withHttpOnlyFalse allows angular to read XSRF cookie. Make sure that Angular makes XHR request with withCreddentials flag set to true.

For more details, you may explore the following

Updated method configure(HttpSecurity http)

 http
            .csrf()
            .ignoringAntMatchers("endpoint-to-be-ignored-for-csrf")
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .and()
            .authorizeRequests()
            .antMatchers("/index").permitAll() 
            .anyRequest().authenticated();


Endpoint specified in antMatchers with permitAll() should not required authentication and antMatchers("/index").permitAll() should work fine.

  • Make sure your security configuration class is annotated with @EnableWebSecurity and @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
  • The security configuration class is in follows the package structure and scanned by Spring. spring-component-scanning

You may find the minimal working example here


SecurityConfiguration.java

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // https://stackoverflow.com/a/56389047/10961238 -> WebSecurity vs HttpSecurity

    // Add this method if .antMatchers("/index").permitAll() does not work
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.debug(true);
//        web
//            .ignoring()
//            .antMatchers("/index");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/index").permitAll() //commenting this line will be results in 403
            .anyRequest().authenticated();
    }

}

SampleController.java

import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller("myController")
public class SampleController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("::::::::::::::::::::::::::::::::Controller:::::::::::::::::::::::::::::::::");
        response.getWriter().print("Hello world!");
        return null;
    }
}

MainApplication.java

import com.example.mappings.controller.SampleController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;

import java.util.Properties;

@SpringBootApplication
public class MappingsApplication {

    public static void main(String[] args) {
        SpringApplication.run(MappingsApplication.class, args);
    }

    @Bean
    public SimpleUrlHandlerMapping sampleServletMapping() {
        System.out.println("::::::::::::::::::::::::::::::::SimpleUrlHandlerMapping:::::::::::::::::::::::::::::::::");
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(Integer.MAX_VALUE - 2);

        Properties urlProperties = new Properties();
        urlProperties.put("/index", sampleController());
        mapping.setMappings(urlProperties);

        return mapping;
    }

    @Bean
    public SampleController sampleController() {
        System.out.println("::::::::::::::::::::::::::::::::Setting SampleController:::::::::::::::::::::::::::::::::");
        return new SampleController();
    }
}

application.properties

spring.security.user.name = user
spring.security.user.password = user
spring.security.user.roles = ADMIN

这篇关于Spring 安全配置不适用于 Spring SimpleUrlHandlerMapping的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆