Rest spring security-根据是否经过身份验证,委托给不同的控制器?
与使用REST控制器不同的是,在每个方法中,我根据当前用户是否经过身份验证而采取不同的操作,我希望根据用户的身份验证状态,委托给完全不同的控制器实现Rest spring security-根据是否经过身份验证,委托给不同的控制器?,rest,spring-mvc,spring-security,Rest,Spring Mvc,Spring Security,与使用REST控制器不同的是,在每个方法中,我根据当前用户是否经过身份验证而采取不同的操作,我希望根据用户的身份验证状态,委托给完全不同的控制器实现 也就是说,我将提供一个包含一组方法签名的接口,每个方法签名都带有一个@RequestMapping注释,然后提供该接口的一个实现用于经过身份验证的用户,另一个实现用于未经身份验证的用户。然后,一些逻辑会为当前用户选择合适的实现并分派给它。我认为这比证明的要容易。这是我的解决方案 首先,我创建了一个包含控制器请求的抽象类,而不是接口: @PreAut
也就是说,我将提供一个包含一组方法签名的接口,每个方法签名都带有一个
@RequestMapping
注释,然后提供该接口的一个实现用于经过身份验证的用户,另一个实现用于未经身份验证的用户。然后,一些逻辑会为当前用户选择合适的实现并分派给它。我认为这比证明的要容易。这是我的解决方案
首先,我创建了一个包含控制器请求的抽象类,而不是接口:
@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);
}
}
@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";
}
}
需要注意的是@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;
}
}
对于非授权用户:
@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安全性注释。起初我以为我可以用@Secured(AuthenticatedVoter.IS\u AUTHENTICATED\u FULLY)
注释AuthenticatedAccountRestController
但是,当映射已在超类中定义时,在子类上添加注释不起作用-请参阅
受这个答案的启发,我在超类上使用了@PreAuthorize
,这样我就可以改变它在子类中的实际行为
同样令人感兴趣的是,已验证的签入AccountRestController
和ForwardingAccountController
-在后者中,我不必担心匿名身份验证令牌
-我为匿名用户获取空值
如果您想进行实验,可以在Github repo中找到这些类
为什么我希望事情会更简单?在JAX-RS中,我能够通过一个资源实现类似的功能,该资源根据用户的状态返回不同的子资源,这些子资源处理实际的请求。我想我可以在春天做一些类似的事情
PS很抱歉使用授权和认证的术语,就好像它们是可互换的术语。我知道您有一个对您有用的答案,但是一个可能感兴趣的解决方案(我想现在只是提供一些信息)是使用Spring自定义映射条件 我们可以定义一个可以用来装饰控制器的注释-可能类似于
@AuthenticatedMapping
@Target( ElementType.TYPE )
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticatedMapping {}
(您可以以不同的方式实现它,并使用枚举值来指示特定的角色等,如果您更喜欢这种粒度,还可以将其设置为方法级注释)
然后,您可以定义一个自定义的RequestCondition
——这是Spring将用来为给定请求制定正确处理程序的类(就像@RequestMapping
注释一样)
使用Spring连接自定义条件后,您可以使用注释装饰任何控制器,Spring将使用该条件路由请求:
@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);
}
}
@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)
- 由于它是一个单一的端点,所以它不处理特定的安全性内容,因此仍然需要将安全性内容放在适当的位置,而不是百分之百地考虑spring安全性身份验证内容
不管怎么说,如果没有其他东西的话,这可能会很有趣:)我不能说我已经尝试过这种方法——因此我无法立即将其与我已经采取的方法进行比较。但它看起来很有趣,值得赏金:)