首先,我了解@PreAuthorize
注释和Expression based access control。
为了学习(以及许多原因),我想要的是:
@AccessControl
)实现了访问控制与角色完全关联的范例。可以在类/类型(REST控制器)上设置注释,在这种情况下,它适用于任何不存在其他此类注释的方法,或者应用于方法(REST端点)。无论是限制还是放宽授权约束,最深的注释总会获胜。作为示例,除了方法上的@AccessControl
注释之外,控制器将具有只能在其角色列表中具有ADMIN的用户访问的端点:
@RestController
@RequestMapping("/admin")
@AccessControl({ Roles.ADMIN })
public class AdminController {
...
}
我目前的犹豫不决,在过去几天阅读了很多内容后,更多的是关于是否要编写自定义请求过滤器或者更确切地说是AOP建议。
使用自定义请求过滤器,我发现自己无法(暂时)确定请求将映射到哪个控制器的哪个方法。注释不在我的手中。
有了AOP建议,我不知道(还)如何以403 Forbidden状态回复客户端。
我的问题直接源于这两点:
答案 0 :(得分:0)
如果您想要遵循AOP建议根目录,我想提供一种方法:
关于这一点,如果使用AOP:
如何从AOP建议中返回HTTP状态代码 当客户端没有时,有效地结束请求的处理 授权? 解决方案:
在您的方面课程中,使用at Advice请执行以下操作:
@Around("execution(* net.my.package.AdminController.*(..)) && args(.., principal)")
public ResponseEntity<?> processRequest(final ProceedingJoinPoint joinPoint, final Principal principal) {
final String controllerMethodName = joinPoint.getSignature().getName();
LOGGER.info("Controller Method name : {}", controllerMethodName);
final boolean isAuthSuccessful = authenticationService.authenticate(principal);//Pass auth details here
if(!isAuthSuccessful) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Request declined"); //End request if auth failed
} else {
try {
return (ResponseEntity<?>)joinPoint.proceed(); //Continue with request
} catch (Throwable e) {
LOGGER.error("Error In Aspect :", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("failed request");
}
}
}
好吧,上面的代码提供了解决您所面临挑战的评论。但是要使此代码工作,请确保执行以下操作:
答案 1 :(得分:0)
事实证明这比我最初想的要简单得多,并且我在不到一天的时间内使用AOP选项完成了它。
这是AccessControl
注释的代码,删除了注释:
@Documented
@Inherited
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface AccessControl {
public String[] value() default {};
}
它可以放在控制器上(参见我原来的帖子/问题)或控制器方法:
@RestController
@RequestMapping("/admin")
@AccessControl({ Roles.ADMIN })
public class AdminController {
// This endpoint has open access: no authorization check will happen.
@AccessControl
@RequestMapping(value = "{id}", method = RequestMethod.GET)
public DummyDto getNoCheck(@PathVariable Integer id) {
return service.get(id);
}
// This endpoint specifically allows access to the "USER" role, which is lower
// than ADMIN in my hierarchy of roles.
@AccessControl(Roles.USER)
@RequestMapping(value = "{id}", method = RequestMethod.GET)
public DummyDto getCheckUser(@PathVariable Integer id) {
return service.get(id);
}
// The authorization check defaults to checking the "ADMIN" role, because there's
// no @AccessControl annotation here.
@RequestMapping(value = "{id}", method = RequestMethod.GET)
public DummyDto getCheckRoleAdmin(@PathVariable Integer id) {
return service.get(id);
}
}
为了执行实际验证,必须回答两个问题:
问题1:要处理哪些方法?
对我而言,答案类似于&#34;我的代码中的所有REST端点&#34;。由于我的代码位于特定的根包中,并且由于我在Spring中使用RequestMapping批注,因此具体的答案以Pointcut规范的形式出现:
@Pointcut("execution(@org.springframework.web.bind.annotation.RequestMapping * *(..)) && within(my.package..*)")
问题2:在运行时确切检查了什么?
我不会把整个代码放在这里,但基本上,答案在于将用户的角色与方法所需的角色(或者如果方法本身不具有访问控制规范的控制器)进行比较。
@Around("accessControlled()")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
...
// Get the roles specified in the access control rule that applies (from the method annotation, or from the controller annotation).
// Get the user roles from the UserDetails previously saved when the user went through the authentication process.
// Check authorizations: does the user have one role that is required? If no, throw an exception. If yes, don't do anything.
// No exception has been thrown: let the method proceed and return its results.
}
在我最初的想法中困扰我的是例外。由于我已经有一个带有@ControllerAdvice
注释的异常映射器类,我只是重用该类将我的特定AccessControlException
映射到403 Forbidden状态代码。
为了检索用户的角色,我使用SecurityContextHolder.getContext().getAuthentication()
恢复身份验证令牌,然后使用authentication.getPrincipal()
检索自定义用户详细信息对象,该对象具有roles
字段我通常在身份验证过程中设置。
代码abbove不能按原样使用(例如,路径映射冲突会发生),但这只是为了传达一般的想法。