我认为这比事实证明要容易。这是我的解决方案。
首先我创建了一个包含控制器请求的抽象类,而不是一个接口:
@PreAuthorize("this.authorized")
public abstract class AccountRestController {
@RequestMapping("/someCommonRequestA")
public abstract String someCommonRequestA();
public boolean getAuthorized() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
return !(authentication == null ||
authentication instanceof AnonymousAuthenticationToken);
}
}
需要注意的是@PreAuthorize注解和getAuthorized()方法。然后我提供了一个类来处理转发到适当的控制器:
@Controller
public class ForwardingAccountController {
@RequestMapping("/account/**")
public String forward(HttpServletRequest request,
Authentication authentication) {
String prefix = authentication != null ? "authenticated" : "anonymous";
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
// You could do a redirect if you wanted to make explicit to the caller what's going on.
return "forward:/" + prefix + "/" + path;
}
}
然后我提供了经过身份验证和匿名的实现,实际行为取决于当前用户的状态。
对于授权用户:
@RestController
@RequestMapping("/authenticated/account")
public class AuthenticatedAccountRestController extends AccountRestController {
@Override
public String someCommonRequestA() {
return "Got authenticated someCommonRequestA";
}
}
对于非授权用户:
@RestController
@RequestMapping("/anonymous/account")
public class AnonymousAccountRestController extends AccountRestController {
@Override
public String someCommonRequestA() {
return "Got anonymous someCommonRequestA";
}
@Override
public boolean getAuthorized() {
return true;
}
}
了解AnonymousAccountRestController 如何通过覆盖getAuthorized() 来关闭授权要求。
棘手的一点是 Spring Security 注释。最初我以为我可以用@Secured(AuthenticatedVoter.IS_AUTHENTICATED_FULLY) 注释AuthenticatedAccountRestController。
但是,当在超类中定义映射时,在子类上添加注释不起作用 - 请参阅 "Spring MVC controller inheritance with spring security"。
受这个 SO 答案的启发,我在超类上使用了@PreAuthorize,这样我就可以改变它在子类中的实际行为。
同样有趣的是 AccountRestController 和 ForwardingAccountController 中的身份验证检查的区别 - 在后者中,我不必担心 AnonymousAuthenticationToken - 对于匿名用户,我得到一个空值。
如果你想进行实验,可以在 Github repo auth-dependent-controllers 中找到这些类。
为什么我希望事情变得更简单?在 JAX-RS 中,我能够通过根据用户状态返回不同子资源的资源实现类似的功能,并且这些子资源处理实际请求。我想我可以在 Spring 中做一些类似的事情。
PS 很抱歉使用授权和认证,好像它们是可互换的术语。