我在 Spring Boot 3.0.2 中遇到了一个非常奇怪的安全问题
当我尝试使用
@PreAuthorize
保护 REST 服务并且该方法包含参数时,我收到以下错误消息:
Name for argument of type [int] not specified, and parameter name information not found in class file either.
如果我删除注释,一切都会恢复正常。
这是我的源代码:
这项工作,方法 listAll 上没有参数:
@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);
}
}
我不知道我能做什么?有人有想法吗?
我的安全过滤器链:
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 Parameter -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,应用程序不会启动。我收到此错误消息:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'metaDataSourceAdvisor' 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 'metaDataSourceAdvisor' 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.
完成,我已经有了注释
编辑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,那么只有一个完全空的对象到达方法“添加”。当然我得到一个 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,"name":"Dollar","iso2":null,"iso3":"USD","symbol":"$","alignment":"RIGHT","exchangeRatio":1.00}
安全获取对象:
{"objectType":"Currency","objectId":"2ea69820-1f8b-46c8-80c1-7e61e2f983fa","objectVersion":null,"createdAt":"2023-03-02T15:44:37.690+01:00","createdBy":null,"updatedAt":"2023-03-02T15:44:37.690+01:00","updatedBy":null,"deleted":false,"deletedAt":null,"deletedBy":null,"deactivated":false,"deactivatedAt":null,"id":null,"name":null,"iso2":null,"iso3":null,"symbol":null,"alignment":"RIGHT","exchangeRatio":null}
编辑 5: 我调试请求并发现了差异。我希望能以任何方式提供帮助。如果 Rest Request 工作,没有 Annotaion
@PreAuthorize("hasAnyRole('api','frontend')")
,正确的抽象类进入 MethodParameter
,因此可以找到参数名称。当我添加注释时,错误的 extendet 类在 MethodParameter
中也带有 final,并且找不到名称。
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;
}
}
编辑5:
我能够隔离问题。但是不知道怎么解决。我注意到这个
$$SpringCGLIB$$
代理类只是为插入另一个方法的其余服务类创建的。
这项工作没有问题:
@RestController
@RequestMapping("/api/businessrole")
@Tag(name = "businessrole")
@Extension
@PreAuthorize("hasAnyRole('api','frontend')")
public class BusinessRoleRestService extends AbstractRestService<BusinessRoleController, Long, BusinessRole> {
public BusinessRoleRestService(BusinessRoleController businessRoleController) {
super(businessRoleController);
}
}
在这里创建了
$$SpringCGLIB$$
代理类,我得到了错误消息。
@RestController
@RequestMapping("/api/businessrole")
@Tag(name = "businessrole")
@Extension
@PreAuthorize("hasAnyRole('api','frontend')")
public class BusinessRoleRestService extends AbstractRestService<BusinessRoleController, Long, BusinessRole> {
public BusinessRoleRestService(BusinessRoleController businessRoleController) {
super(businessRoleController);
}
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Success")
})
@Operation(summary = "",
description = "")
@GetMapping("/types")
public List<String> listAllSubclasses() {
return this.controller.findAllSubclasses();
}
}
我找到了解决方案。使用
ClassUtils.getUserClass(clazz);
我得到了正确的类,并且可以使用 RequestMappingHandlerMapping
手动注册方法。现在一切正常。我仍然认为这是一个错误。但我不知道在哪里。