不是在每个方法中都有一个REST控制器,而是取决于当前用户是否经过身份验证而采取不同的操作我想根据用户的身份验证状态委托给完全不同的控制器实现。
即。我提供了一个包含一组方法签名的接口,每个方法签名都带有@RequestMapping
注释,然后提供此接口的一个实现,用于经过身份验证的用户,另一个实现用于未经过身份验证的用户。然后,一些逻辑将为当前用户选择适当的实现并发送给它。
答案 0 :(得分:4)
我知道你有一个适合你的答案,但是一个可能感兴趣的解决方案(我现在只是为了获取信息)是使用Spring自定义映射条件。
我们可以定义一个可以用来装饰我们的控制器的注释 - 可能像@AuthenticatedMapping
@Target( ElementType.TYPE )
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticatedMapping {}
(你可以用不同的方式实现它,并且有一个枚举值来表示特定的角色等,如果你更喜欢这种粒度,也可以将它设置为METHOD级别注释)
然后你可以定义一个自定义RequestCondition
- 这是Spring将用作为给定请求设计正确处理程序的一部分的类(就像@RequestMapping
注释一样)
public class AuthenticatedMappingRequestCondition implements RequestCondition<AuthenticatedMappingRequestCondition> {
@Override public AuthenticatedMappingRequestCondition getMatchingCondition( HttpServletRequest request ) {
AuthenticatedMappingRequestCondition condition = null;
//Check the user is authenticated, if so return this condition:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if( (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)){
condition = this;
}
return condition;
}
//
//TODO other methods need to be implemented here - all pretty simple
//
}
所以现在我们有了映射条件,我们只需要扩展标准的Spring RequestMappingHandlerMapping
,以便在考虑映射决策时包含我们的自定义条件:
public class AuthenticatedMappingRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
AuthenticatedMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, AuthenticatedMapping.class);
return (typeAnnotation != null) ? new AuthenticatedMappingRequestCondition() : null;
}
}
使用Spring连接自定义条件后,您可以使用注释装饰任何控制器,Spring将使用条件来路由请求:
@RestController
@AuthenticatedMapping
@RequestMapping("/account")
public class AuthenticatedAccountRestController {
@RequestMapping("/someCommonRequestA")
public String someCommonRequestA() {
return "Got authenticated someCommonRequestA";
}
}
@RestController
@RequestMapping("/account")
public class AnonymousAccountRestController {
@RequestMapping("/someCommonRequestA")
public String someCommonRequestA() {
return "Got anonymous someCommonRequestA";
}
}
一些警告:
@AuthenticatedMapping( Roles.ANONYMOUS )
不建议它比你拥有的方法更好,但我不得不使用这种方法基于子域进行自定义路由(我写了here - 这就是上面的代码所基于的),当我不得不将它推广到许多控制器/端点时,我发现它是一个非常好的,惯用的Spring-y解决方案,因为样板被抽象到Spring机器并且控制器装饰得很好。
无论如何,如果没别的话可能会很有趣:)
答案 1 :(得分:2)
我认为这比证明的要容易。这是我的解决方案。
首先,我创建了一个包含控制器请求的抽象类,而不是接口:
@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注释。最初我以为我可以用AuthenticatedAccountRestController
注释@Secured(AuthenticatedVoter.IS_AUTHENTICATED_FULLY)
。
但是,当在超类中定义映射时,在子类上添加注释不起作用 - 请参阅"Spring MVC controller inheritance with spring security"。
受到SO答案的启发,我在超类上使用了@PreAuthorize
,这样我就可以改变它在子类中的实际行为。
同样令人感兴趣的是AccountRestController
和ForwardingAccountController
中经过身份验证的检查的差异 - 后者我不必担心AnonymousAuthenticationToken
- 我为匿名用户获取null
如果您想进行实验,可以在Github repo auth-dependent-controllers中找到这些课程。
为什么我希望事情更简单?在JAX-RS中,我能够使用返回不同子资源的资源实现类似的功能,具体取决于用户的状态,这些子资源处理实际请求。我以为我能在春天做一些相似的事情。
PS抱歉使用授权和身份验证,就像它们是可互换的术语一样。