Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/eclipse/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Rest spring security-根据是否经过身份验证,委托给不同的控制器?_Rest_Spring Mvc_Spring Security - Fatal编程技术网

Rest spring security-根据是否经过身份验证,委托给不同的控制器?

Rest spring security-根据是否经过身份验证,委托给不同的控制器?,rest,spring-mvc,spring-security,Rest,Spring Mvc,Spring Security,与使用REST控制器不同的是,在每个方法中,我根据当前用户是否经过身份验证而采取不同的操作,我希望根据用户的身份验证状态,委托给完全不同的控制器实现 也就是说,我将提供一个包含一组方法签名的接口,每个方法签名都带有一个@RequestMapping注释,然后提供该接口的一个实现用于经过身份验证的用户,另一个实现用于未经身份验证的用户。然后,一些逻辑会为当前用户选择合适的实现并分派给它。我认为这比证明的要容易。这是我的解决方案 首先,我创建了一个包含控制器请求的抽象类,而不是接口: @PreAut

与使用REST控制器不同的是,在每个方法中,我根据当前用户是否经过身份验证而采取不同的操作,我希望根据用户的身份验证状态,委托给完全不同的控制器实现


也就是说,我将提供一个包含一组方法签名的接口,每个方法签名都带有一个
@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安全性身份验证内容
不是说这是一种比您现有的更好的方法,但我不久前不得不使用这种方法基于子域进行自定义路由(我编写了它——这就是上面代码的基础),我发现它非常不错,当样板文件被抽象为Spring机械,控制器被很好地装饰时,我不得不将它推广到许多控制器/端点,这就是惯用的Spring-y解决方案


不管怎么说,如果没有其他东西的话,这可能会很有趣:)

我不能说我已经尝试过这种方法——因此我无法立即将其与我已经采取的方法进行比较。但它看起来很有趣,值得赏金:)