Spring Boot 3.0.2中出现了奇怪的安全错误。

huangapple go评论69阅读模式
英文:

Strange Security Error in Spring Boot 3.0.2

问题

以下是您提供的内容的翻译:

我在Spring Boot 3.0.2中遇到了一个非常奇怪的安全问题

当我尝试使用`@PreAuthorize`来保护REST服务并且方法包含参数时我会收到以下错误消息

    未指定类型为[int]的参数名称并且在类文件中也找不到参数名称信息

如果我移除这个注解一切又恢复正常

以下是我的源代码

这个工作方法列表上没有参数

    @RestController
    @RequestMapping("/api/currency")
    @PreAuthorize("hasAnyRole('api','frontend')")  // <<--
    public class CurrencyRestService {

        @GetMapping("/")
        public Page<Currency> listAll() { // <<--
            return currencyController.listAll(0, 100);
        }
    }

这个也有效没有PreAuthorize但有参数

    @RestController
    @RequestMapping("/api/currency")
    //@PreAuthorize("hasAnyRole('api','frontend')") // <<--
    public class CurrencyRestService {

        @GetMapping("/")
        public Page<Currency> listAll(@RequestParam(defaultValue = "0") int page,      // <<--
                                  @RequestParam(defaultValue = "100") int size) {  // <<--
            return currencyController.listAll(page, size);
        }
    }

这个不工作

    @RestController
    @RequestMapping("/api/currency")
    @PreAuthorize("hasAnyRole('api','frontend')")  // <<--
    public class CurrencyRestService {

        @GetMapping("/")
        public Page<Currency> listAll(@RequestParam(defaultValue = "0") int page,      // <<--
                                  @RequestParam(defaultValue = "100") int size) {  // <<--
            return currencyController.listAll(page, size);
        }
    }

我不知道我该怎么办有人有什么想法吗

我的SecurityFilterChain

    http.securityMatcher("/api/**")
                .oauth2ResourceServer()
                .jwt()
                .jwtAuthenticationConverter(authenticationConverter);

    http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.csrf().disable();

    http.securityMatcher("/api/**")
                .authorizeHttpRequests()
                    .requestMatchers("/api/**")
                    .authenticated();
    http.build();

编辑

这也不起作用

    @RestController
    @RequestMapping("/api/currency")
    @PreAuthorize("hasAnyRole('api','frontend')")  // <<--
    public class CurrencyRestService {

        @GetMapping("/")
        public Page<Currency> listAll(@RequestParam(defaultValue = "0",
                                            name = "page") int page,      // <<--
                                  @RequestParam(defaultValue = "100",
                                            name = "size") int size) {  // <<--
            return currencyController.listAll(page, size);
        }
    }

还有javac的参数-g:vars和/-parameters也没有帮助

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
            <forceJavacCompilerUse>true</forceJavacCompilerUse>
            <compilerArgs>
                <arg>-g:vars</arg>
                <arg>-parameters</arg>
            </compilerArgs>
            <source>${maven.compiler.source}</source>
            <target>${maven.compiler.target}</target>
        </configuration>
    </plugin>

编辑2

如果我使用@EnableGlobalMethodSecurity应用程序将无法启动我收到以下错误消息

    上下文初始化期间遇到异常 - 取消刷新尝试org.springframework.beans.factory.support.BeanDefinitionOverrideException
    名为'metaDataSourceAdvisor'的bean定义无效定义在null中无法注册bean定义
    [根bean[org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor]作用域=抽象=falselazyInit=nullautowireMode=0dependencyCheck=0autowireCandidate=trueprimary=falsefactoryBeanName=nullfactoryMethodName=nullinitMethodNames=nulldestroyMethodNames=null]
    用于bean'metaDataSourceAdvisor',因为已经有一个
    [根bean[org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor]作用域=抽象=falselazyInit=nullautowireMode=0dependencyCheck=0autowireCandidate=trueprimary=falsefactoryBeanName=nullfactoryMethodName=nullinitMethodNames=nulldestroyMethodNames=null]绑定

已经完成我已经添加了注释

编辑3

如果我不使用@RequestParam而是这样做它可以工作

    @RestController
    @RequestMapping("/api/currency")
    @PreAuthorize("hasAnyRole('api','frontend')")  // <<--
    public class CurrencyRestService {

        @GetMapping("/")
        public Page<Currency> listAll(Map<String, String> reqParam) { // <<--
            int page = 0;
            int size = 100;
            try {
                page = Integer.parseInt(reqParam.get("page"));
                size = Integer.parseInt(reqParam.get("size"));
            } catch (Exception e) {
                page = 0;
                size = 100;
            }
            if (size <= 0) {
                size = 100;
            }
            return currencyController.listAll(page, size);
        }
    }

编辑4

问题更奇怪如果我现在进行POST请求那么只会在"add"方法中收到一个完全空的对象当然我会收到一个NotNull错误如果我关闭安全性对象会干净而完整地传递

    @PostMapping("/")
    @PreAuthorize("hasAnyRole('api-write','frontend')")
    public ResponseEntity add(@RequestBody Currency newObject) {
            System.err.println(newObject.toString());
            return new ResponseEntity(this.controller.add(newObject), HttpStatus.OK);
    }

错误消息

    ConstraintViolationImpl{interpolatedMessage='darf nicht null sein', propertyPath=updatedBy, rootBeanClass=class de.bewidata.framework.entity.zrentity.Currency, messageTemplate='{jakarta.validation.constraints.NotNull.message}'}

发送对象

    {"objectType":"Currency","objectId":"2ea69820-1f8b-46c8-80c1-7e61e2f983fa","objectVersion":0,"createdAt":"2023-03-02T15:44:37.690+01:00","createdBy":"system","updatedAt":"2023-03-02T15:44:37.690+01:00","updatedBy":"system","deleted":false,"deletedAt":null,"deletedBy":null,"deactivated":false,"deactivatedAt":null,"id":0

<details>
<summary>英文:</summary>

I have a very strange security issue in Spring Boot 3.0.2

When I try to secure a REST service with `@PreAuthorize` and the method contains parameters I get the following error message:

    Name for argument of type [int] not specified, and parameter name information not found in class file either.

If I remove the annotation, everything works again.

Here is my source code:

This work, no parameters on method listAll:

    @RestController
    @RequestMapping(&quot;/api/currency&quot;)
    @PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)  // &lt;&lt;--
    public class CurrencyRestService {
    
        @GetMapping(&quot;/&quot;)
        public Page&lt;Currency&gt; listAll() { // &lt;&lt;--
            return currencyController.listAll(0, 100);
        }
    }

This work too, no PreAuthorize, but with parameters

    @RestController
    @RequestMapping(&quot;/api/currency&quot;)
    //@PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;) // &lt;&lt;--
    public class CurrencyRestService {
    
        @GetMapping(&quot;/&quot;)
        public Page&lt;Currency&gt; listAll(@RequestParam(defaultValue = &quot;0&quot;) int page,      // &lt;&lt;--
                                      @RequestParam(defaultValue = &quot;100&quot;) int size) {  // &lt;&lt;--
            return currencyController.listAll(page, size);
        }
    }

This not work:

    @RestController
    @RequestMapping(&quot;/api/currency&quot;)
    @PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)  // &lt;&lt;--
    public class CurrencyRestService {
    
        @GetMapping(&quot;/&quot;)
        public Page&lt;Currency&gt; listAll(@RequestParam(defaultValue = &quot;0&quot;) int page,      // &lt;&lt;--
                                      @RequestParam(defaultValue = &quot;100&quot;) int size) {  // &lt;&lt;--
            return currencyController.listAll(page, size);
        }
    }

I don&#39;t know what I can do? Did anyone had an idea?

My SecurityFilterChain:

    http.securityMatcher(&quot;/api/**&quot;)
    			.oauth2ResourceServer()
    			.jwt()
    			.jwtAuthenticationConverter(authenticationConverter);

    http.sessionManagement()
    			.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.csrf().disable();

    http.securityMatcher(&quot;/api/**&quot;)
   				.authorizeHttpRequests()
   					.requestMatchers(&quot;/api/**&quot;)
   					.authenticated();
    http.build();

EDIT:

This also doesn&#39;t work:

    @RestController
    @RequestMapping(&quot;/api/currency&quot;)
    @PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)  // &lt;&lt;--
    public class CurrencyRestService {
    
        @GetMapping(&quot;/&quot;)
        public Page&lt;Currency&gt; listAll(@RequestParam(defaultValue = &quot;0&quot;,
												    name = &quot;page&quot;) int page,      // &lt;&lt;--
                                      @RequestParam(defaultValue = &quot;100&quot;,
												    name = &quot;size&quot;) int size) {  // &lt;&lt;--
            return currencyController.listAll(page, size);
        }
    }

Also javac Parameter -g:vars and / or -parameters not help

    &lt;plugin&gt;
    	&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
    	&lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
    	&lt;version&gt;3.8.1&lt;/version&gt;
    	&lt;configuration&gt;
			&lt;forceJavacCompilerUse&gt;true&lt;/forceJavacCompilerUse&gt;
    		&lt;compilerArgs&gt;
    			&lt;arg&gt;-g:vars&lt;/arg&gt;
    			&lt;arg&gt;-parameters&lt;/arg&gt;
    		&lt;/compilerArgs&gt;
    		&lt;source&gt;${maven.compiler.source}&lt;/source&gt;
    		&lt;target&gt;${maven.compiler.target}&lt;/target&gt;
    	&lt;/configuration&gt;
    &lt;/plugin&gt;

EDIT 2:

If I use @EnableGlobalMethodSecurity the Application don&#39;t start. I get this error message:

    Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.support.BeanDefinitionOverrideException:
    Invalid bean definition with name &#39;metaDataSourceAdvisor&#39; defined in null: Cannot register bean definition
    [Root bean: class [org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null]
    for bean &#39;metaDataSourceAdvisor&#39; since there is already 
    [Root bean: class [org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] bound.

Is done, I already had the annotation.

EDIT 3:

If I don&#39;t use @RequestParam but do it this way it works:

    @RestController
    @RequestMapping(&quot;/api/currency&quot;)
    @PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)  // &lt;&lt;--
    public class CurrencyRestService {
    
        @GetMapping(&quot;/&quot;)
        public Page&lt;Currency&gt; listAll(Map&lt;String, String&gt; reqParam) { // &lt;&lt;--
    		int page = 0;
    		int size = 100;
    		try {
    			page = Integer.parseInt(reqParam.get(&quot;page&quot;));
    			size = Integer.parseInt(reqParam.get(&quot;size&quot;));
    		} catch (Exception e) {
    			page = 0;
    			size = 100;
    		}
    		if (size &lt;= 0) {
    			size = 100;
    		}
    		return currencyController.listAll(page, size);
        }
    }

EDIT 4:

The problem is even stranger. If I now make a POST, then only a completely empty object arrives in the method &quot;add&quot;. Of course I get a NotNull Error. If I turn off the security, the object arrives clean and complete.

    @PostMapping(&quot;/&quot;)
    @PreAuthorize(&quot;hasAnyRole(&#39;api-write&#39;,&#39;frontend&#39;)&quot;)
    public ResponseEntity add(@RequestBody Currency newObject) {
    		System.err.println(newObject.toString());
    		return new ResponseEntity(this.controller.add(newObject), HttpStatus.OK);
    }

Error message:

    ConstraintViolationImpl{interpolatedMessage=&#39;darf nicht null sein&#39;, propertyPath=updatedBy, rootBeanClass=class de.bewidata.framework.entity.zrentity.Currency, messageTemplate=&#39;{jakarta.validation.constraints.NotNull.message}&#39;}

Send Object:

    {&quot;objectType&quot;:&quot;Currency&quot;,&quot;objectId&quot;:&quot;2ea69820-1f8b-46c8-80c1-7e61e2f983fa&quot;,&quot;objectVersion&quot;:0,&quot;createdAt&quot;:&quot;2023-03-02T15:44:37.690+01:00&quot;,&quot;createdBy&quot;:&quot;system&quot;,&quot;updatedAt&quot;:&quot;2023-03-02T15:44:37.690+01:00&quot;,&quot;updatedBy&quot;:&quot;system&quot;,&quot;deleted&quot;:false,&quot;deletedAt&quot;:null,&quot;deletedBy&quot;:null,&quot;deactivated&quot;:false,&quot;deactivatedAt&quot;:null,&quot;id&quot;:0,&quot;name&quot;:&quot;Dollar&quot;,&quot;iso2&quot;:null,&quot;iso3&quot;:&quot;USD&quot;,&quot;symbol&quot;:&quot;$&quot;,&quot;alignment&quot;:&quot;RIGHT&quot;,&quot;exchangeRatio&quot;:1.00}

Getted Object by security on:

    {&quot;objectType&quot;:&quot;Currency&quot;,&quot;objectId&quot;:&quot;2ea69820-1f8b-46c8-80c1-7e61e2f983fa&quot;,&quot;objectVersion&quot;:null,&quot;createdAt&quot;:&quot;2023-03-02T15:44:37.690+01:00&quot;,&quot;createdBy&quot;:null,&quot;updatedAt&quot;:&quot;2023-03-02T15:44:37.690+01:00&quot;,&quot;updatedBy&quot;:null,&quot;deleted&quot;:false,&quot;deletedAt&quot;:null,&quot;deletedBy&quot;:null,&quot;deactivated&quot;:false,&quot;deactivatedAt&quot;:null,&quot;id&quot;:null,&quot;name&quot;:null,&quot;iso2&quot;:null,&quot;iso3&quot;:null,&quot;symbol&quot;:null,&quot;alignment&quot;:&quot;RIGHT&quot;,&quot;exchangeRatio&quot;:null}


EDIT 5:
I debug the request and found a difference. I hope that help in any way. If the Rest Request work, without the Annotaion `@PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)`, the right abstract class come into the `MethodParameter`, so the parameter names could found. When I add the annotation, the wrong extendet class is in the `MethodParameter` also with final, and the names couldn&#39;t found.

    package org.springframework.web.method.annotation;
    
    public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
        private NamedValueInfo getNamedValueInfo(MethodParameter parameter) { // WORK without Security  parameter.executable = public org.springframework.data.domain.Page com.example.framework.AbstractRestService.listAll(int,int)
                                                                              // NOT WORK with Security parameter.executable = public final org.springframework.data.domain.Page com.example.framework.BusinessRoleRestService$$SpringCGLIB$$0.listAll(int,int)
            NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
            if (namedValueInfo == null) {
                namedValueInfo = createNamedValueInfo(parameter);
                namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
                this.namedValueInfoCache.put(parameter, namedValueInfo);
            }
            return namedValueInfo;
        }
    }

EDIT 5:

I was able to isolate the problem. But do not know how to solve it. I noticed that this `$$SpringCGLIB$$` proxy class is only created for the rest of the service classes where another method is inserted.

This work without Problems:

    @RestController
    @RequestMapping(&quot;/api/businessrole&quot;)
    @Tag(name = &quot;businessrole&quot;)
    @Extension
    @PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)
    public class BusinessRoleRestService extends AbstractRestService&lt;BusinessRoleController, Long, BusinessRole&gt; {
    
        public BusinessRoleRestService(BusinessRoleController businessRoleController) {
            super(businessRoleController);
        }
    }

Here the `$$SpringCGLIB$$` proxy class created, and I get the Error Message.

    @RestController
    @RequestMapping(&quot;/api/businessrole&quot;)
    @Tag(name = &quot;businessrole&quot;)
    @Extension
    @PreAuthorize(&quot;hasAnyRole(&#39;api&#39;,&#39;frontend&#39;)&quot;)
    public class BusinessRoleRestService extends AbstractRestService&lt;BusinessRoleController, Long, BusinessRole&gt; {
    
        public BusinessRoleRestService(BusinessRoleController businessRoleController) {
            super(businessRoleController);
        }
    
        @ApiResponses(value = {
                @ApiResponse(responseCode = &quot;200&quot;,
                             description = &quot;Success&quot;)
        })
        @Operation(summary = &quot;&quot;,
                   description = &quot;&quot;)
        @GetMapping(&quot;/types&quot;)
        public List&lt;String&gt; listAllSubclasses() {
            return this.controller.findAllSubclasses();
        }
    }

</details>


# 答案1
**得分**: 0

我找到了一个解决方案使用 `ClassUtils.getUserClass(clazz);` 我得到了正确的类并且可以手动注册方法到 `RequestMappingHandlerMapping`。现在一切都正常工作我仍然认为这是一个 Bug但我不知道在哪里

<details>
<summary>英文:</summary>

I found a solution. With `ClassUtils.getUserClass(clazz);` I get the right class and could register manualy the methods with an `RequestMappingHandlerMapping`. Now all work. I still thing it is a Bug. But I don&#39;t know where. 

</details>



huangapple
  • 本文由 发表于 2023年3月3日 20:55:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75627353.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定