如何以清晰的方式在中间件中注入一个仓库或服务?

huangapple go评论83阅读模式
英文:

How to inject a repo or service in a middleware in a clean way?

问题

我目前正在开发一个Web应用程序,并使用Go中的中间件实现用户身份验证系统。我想知道在中间件中注入存储库或服务的最佳方法是什么。

我之所以这样做是因为我想在每个用户身份验证中使用存储库的方法。然而,我不确定在中间件中注入存储库或服务是否是一种良好的做法。

有没有人对如何以一种清晰和可维护的方式实现这一点有任何建议或最佳实践?

我已经阅读了Stackoverflow上的这个问题(https://stackoverflow.com/questions/37142227/calling-service-repository-methods-in-asp-net-core-middleware),但它似乎与C#相关,而不一定是一个设计问题。

提前感谢您的见解。

英文:

I'm currently working on a web application and implementing a user authentication system using middleware in Go. I'm wondering what the best way is to inject a repository or service in a middleware for user authentication.

The reason I want to do this is because I want to use a repository's method for every user authentication. However, I'm not sure if it's clean to inject a repository or service in a middleware.

Does anyone have any suggestions or best practices for how to do this in a clean and maintainable way?

I have read this question on Stackoverflow but it seems to be related to C# and not necessarily a design issue.

Thanks in advance for your insights.

答案1

得分: 1

可能有一百种以上的方式来回答你的问题,但我会根据我的经验尝试给出一个答案。
首先,如果我理解你的问题,你需要知道如何在中间件中注入一个仓库,然后判断这是否是一个好的实践

关于你的第一个问题

type AuthenticationRepository interface {
	GetApiKey(location string) (string, error)
}

func AuthorizationMiddleware(authRepo AuthenticationRepository) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从头部获取位置和密钥
		location := c.GetHeader("Host")
		apiKey := c.GetHeader("Key")

		// 使用仓库从位置获取API密钥
		expectedKey, err := authRepo.GetApiKey(location)
		if err != nil {
			log.Errorf("尝试获取API密钥时出现意外错误:%v", err)
			c.AbortWithStatus(http.StatusServiceUnavailable)
			return
		}

		if expectedKey != apiKey {
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		// 调用下一个处理程序
		c.Next()
	}
}

通过这个基本示例,你可以在你的仓库中做你需要的事情。

现在关于你的第二个问题,这样做是一个好的实践吗?如果你只有少量的用户或服务,是的,你不会有问题。
但是,如果你想要一个可扩展的基础架构,拥有大量的用户:不,这不是一个好的实践

**为什么?**因为你的所有服务都必须从你的仓库中检索信息。所以你引入了一个单点故障和大量的延迟到你的所有端点。
你可以进行一些缓存,但是如果你有大量的用户,管理起来会很困难。

为了解决这个问题,你需要知道认证授权的区别:认证验证用户或服务的身份,授权确定他们的访问权限。

所以,例如,你可以使用用户的用户名/密码(或者服务的client_id/client_secret)来认证用户,然后,在认证之后,你的授权服务将提供一个JWT令牌。这个令牌是签名的,可以被任何拥有相应公钥的服务验证。因此,你不需要在每个请求中访问特定的服务/存储,因为通过这个公钥,你可以授权访问请求(根据其他已验证的令牌声明)。

关于JWT和获取它的流程的一些文档:

所以,所有这些都取决于用户的数量 如何以清晰的方式在中间件中注入一个仓库或服务?

英文:

There are maybe more than a hundred ways to answer to your question, but I'll try to make an answer based on my experiences.
First, if I understood your question, you need to know how to inject a repository on a middleware then after, to know if it's a good practice.

About your first point:

type AuthenticationRepository interface {
	GetApiKey(location string) (string, error)
}

func AuthorizationMiddleware(authRepo AuthenticationRepository) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Get the location and key from headers
		location := c.GetHeader("Host")
		apiKey := c.GetHeader("Key")

		// Use the repository to get the API key from location
		expectedKey, err := authRepo.GetApiKey(location)
		if err != nil {
			log.Errorf("Unexpected error when trying to get api key: %v", err)
			c.AbortWithStatus(http.StatusServiceUnavailable)
			return
		}

		if expectedKey != apiKey {
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		// Call the next handler
		c.Next()
	}
}

With this basic example, you can do what you need on your repository.

Now about your second point, is it a good practice to do that? If you have only few users or services, yes, you will not have problems.
But, if you want to have a scalable infrastructure with a lot of users: no, this is not a good practice.

Why? Because all your services must retrieve information from your repository. So you are introducing a SPOF and a lot of latency on all your endpoints.
You can do some caching, but it will be hard to manage if you have a large amount of users.

To handle this, you will need to know the difference between authentication and authorization: authentication verifies the identity of a user or service, and authorization determines their access rights.

So, for example, you can authenticate a user with his username/password (or a service with his client_id/client_secret), then, after authentication your authorization service will provide a JWT token. This token is signed and can be verified by any service that have the corresponding public key. So you don't need to reach a specific service/storage from a repository on every request, because with this public key, you can authorize access requests (depending on additional verified token claims).

Some documentation about JWT and flow to get it:

So, all of that depend on the volume of users 如何以清晰的方式在中间件中注入一个仓库或服务?

huangapple
  • 本文由 发表于 2023年4月3日 01:01:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75913434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定