【问题标题】:How to handle JWT Authentication with Spring when implementing a CQRS pattern?实现 CQRS 模式时如何使用 Spring 处理 JWT 身份验证?
【发布时间】:2018-06-14 15:06:09
【问题描述】:

使用最新的 Spring Cloud 和 Spring Boot,我有了一个带有 Zuul 网关的微服务布局。在用户发送获取请求的那一刻,他们的 JWT 令牌被添加到请求中,然后转到微服务进行身份验证,一切照常进行。这一切都完美无缺。

在处理 POST/PATCH/DELETE 请求时,我有点卡住了。这些不会直接进入它们注定要使用的微服务,而是进入消息队列。该队列包含一个简单的 POJO,其中包含一个任务和有关要执行的任务的信息,以及用户 JWT。

当接收微服务从队列中提取消息并开始处理它时,从技术上讲,用户不会像使用 GET 请求那样登录到微服务。这使得需要知道用户是谁的事情变得困难。当然,每次我需要知道这个人是谁时,我都可以查找它们,但这似乎很笨拙。

我考虑过为 POST/PATCH/DELETE 命令创建一个 REST 控制器,并让队列侦听器从这些命令中调用自己,从任务中添加令牌。就 Spring 安全性而言,这实际上与 GET 请求相同。

这是正确的模式吗?还是有一种简单的编程方式让用户使用 JWT 登录?我看过一些使用用户名/密码的示例,但不确定如何将其转录为使用 JWT。

【问题讨论】:

  • 我真的很喜欢你给自己打电话的解决方案。它很优雅,并为您的服务提供异步和同步能力。还有其他方法可以进行异步,但鉴于您已经有一个可以使用的排队基础设施,这是一种将其连接到 REST 控制器的好方法。

标签: java spring spring-security jwt cqrs


【解决方案1】:

感谢Andy Brown 确认我这样做并不是完全疯了。对于任何感兴趣的人来说,这是一个非常简单的解决方案,如下所示:

队列服务只是监听事件(在本例中来自 AWS SQS),一旦有事件通过,它就会被触发到命令控制器进行处理。

@Service
@EnableSqs
public class QueueListener {

    private final static String SERVICE = "http://instance-service/instances";

    @Autowired
    private JsonTransformService jsonTransformService;

    @Autowired
    private RestTemplate restTemplate;

    @MessageMapping("${queues.instanceEvents}")
    public void instanceCommandHandler(String payload) {

        // Transform the payload to the object so we can get the preset JWT
        Instance instance = jsonTransformService.read(Instance.class, payload);

        // Load the JWT into the internal request header, without this a 403 is thrown
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + instance.getUserToken());
        HttpEntity<String> instanceEntity = new HttpEntity<>(payload, headers);

        // Decide and set where to fire the request to
        String endpoint;
        switch (instance.getSwordfishCommand()) {
            case "start": {
                endpoint = "/start";
                break;
            }
            case "stop": {
                endpoint = "/stop";
                break;
            }
            case "create": {
                endpoint = "/create";
                break;
            }
            case "reboot": {
                endpoint = "/reboot";
                break;
            }
            case "terminate": {
                endpoint = "/terminate";
                break;
            }
            default: {
                endpoint = "/error";
            }
        }

        // Fire the initial string payload through to the correct controller endpoint
        restTemplate.exchange(SERVICE + endpoint, HttpMethod.POST, instanceEntity, String.class);
    }
}

还有一个非常简单的 REST 控制器,用于执行任务

@RestController
@RequestMapping("/instances")
public class InstanceCommandController {

    @Autowired
    private EC2Create ec2Create;

    @Autowired
    private EC2Start ec2Start;

    @Autowired
    private EC2Stop ec2Stop;

    @Autowired
    private EC2Reboot ec2Reboot;

    @Autowired
    private EC2Terminate ec2Terminate;

    @Autowired
    private JsonTransformService jsonTransformService;

    @PostMapping("/create")
    public void create(@RequestBody String payload) {
        ec2Create.process(jsonTransformService.read(Instance.class, payload));
    }

    @PostMapping("/start")
    public void start(@RequestBody String payload) {
        ec2Start.process(jsonTransformService.read(Instance.class, payload));
    }

    @PostMapping("/stop")
    public void stop(@RequestBody String payload) {
        ec2Stop.process(jsonTransformService.read(Instance.class, payload));
    }

    @PostMapping("/reboot")
    public void reboot(@RequestBody String payload) {
        ec2Reboot.process(jsonTransformService.read(Instance.class, payload));
    }

    @PostMapping("/terminate")
    public void terminate(@RequestBody String payload) {
        ec2Terminate.process(jsonTransformService.read(Instance.class, payload));
    }
}

这很好地遵循了 CQRS 模式,同时仍然在每次调用时对用户进行身份验证。对我来说这非常有用,因为我有一个 AmazonEC2Async 客户端,它在每个请求中使用用户自己的访问权限和秘密令牌。

为帮助干杯!

【讨论】:

    猜你喜欢
    • 2017-11-02
    • 2020-09-19
    • 2015-01-21
    • 2018-10-21
    • 2017-05-29
    • 2018-10-27
    • 2015-05-31
    • 1970-01-01
    • 2021-04-26
    相关资源
    最近更新 更多