微服务安全

6
在过去的几天中,我一直在使用微服务模式进行开发,一切都进展顺利,但是安全性似乎使我感到困扰。 所以,如果我可以提��个问题: 如何在个别服务上处理用户身份验证?目前,我将请求传递给网关 API,然后连接到服务。 编辑后的问题请参见下文 考虑到个别服务不应该了解彼此。网关是这样的聚合器。 当前架构 enter image description here 以下是模拟请求的小段代码: 前端 - 客户端应用
public class EntityRepository<T>
{
    private IGateway _gateway = null;
    public EntityRepository(IGateway gateway)
    {
        this._gateway = gateway;
    }
    public IEnumerable<T> FindAll()
    {
        return this._gateway.Get(typeof(T)).Content.ReadAsAsync<IEnumerable<T>>().Result;
    }
    public T FindById(int id)
    {
        return this._gateway.Get(typeof(T)).Content.ReadAsAsync<T>().Result;
    }
    public void Add(T obj)
    {
        this._gateway.Post(typeof(T), obj);
    }
    public void Update(T obj)
    {
        this._gateway.Post(typeof(T), obj);
    }
    public void Save(T obj)
    {
        this._gateway.Post(typeof(T), obj);
    }
}


   //Logic lives elsewhere
   public HttpResponseMessage Get(Type type)
   {
      return Connect().GetAsync(Path(type)).Result;
   }
   public HttpResponseMessage Post(Type type, dynamic obj)
   {
      return Connect().PostAsync(Path(type), obj);
   }
    private string Path(Type type)
    {
        var className = type.Name;
        return "api/service/" + Application.Key + "/" + className;
    }
    private HttpClient Connect()
    {
        var client = new HttpClient();
        client.BaseAddress = new Uri("X");

        // Add an Accept header for JSON format.
         client.DefaultRequestHeaders.Accept.Add(
         new MediaTypeWithQualityHeaderValue("application/json"));

        return client;
    }

我使用泛型来确定当它到达网关时需要触发哪个服务。因此,如果TypeCategory,它将触发Category服务,从而调用:

public IEnumerable<dynamic> FindAll(string appKey, string cls)
{
    var response = ConnectTo.Service(appKey, cls);
    return (appKey == Application.Key) ? (response.IsSuccessStatusCode) ? response.Content.ReadAsAsync<IEnumerable<dynamic>>().Result : null : null;
}

网关不包含类型的物理文件/类。

在一些代码之后,我希望有人能给我一个小的演示或者最佳方法来处理当前架构下的安全性/用户认证。

情景1:用户访问Web应用并登录,此时用户的加密电子邮件和密码被发送到Gateway API,然后传递到User Service,并决定用户是否经过身份验证。现在,我想获取用户接收的所有消息,但不能在网关中说用户已经通过身份验证,因为这样做无法解决在Gateway API之外调用Message Service的问题。

我也不能为每个单独的服务添加身份验证,因为这将需要所有相关服务与User Service通信,这违背了该模式的目的。

修复方法:只允许网关调用服务。禁止对网关之外的服务进行请求。

我知道安全是一个广泛的话题,但在当前情况下,我希望有人可以指导我采取最佳行动来解决这个问题。

目前,我已经在所有应用程序中硬编码了一个Guid,这将在应用程序相等时获取数据。


1
有关身份验证和授权的问题有成千上万个,你的研究显示了什么?当然,任何静态的东西(比如GUID)都不具备安全性,因为有人可以简单地从网络流量中嗅探到GUID并构建自己的应用程序,该应用程序也会发送相同的GUID。 - CodeCaster
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Tez Wingfield
抱歉,我花了很多时间编码 - 现在有点脑子一片空白哈哈。给我几分钟重新表达一下。 - Tez Wingfield
1
你使用微服务实现你的服务对于客户来说是无关紧要的。客户所看到的只有网关,因此他们会对网关进行身份验证。或者你的意思是网关应该是透明的,每个在其后面的服务都进行自己的身份验证和授权? - CodeCaster
@CodeCaster 感谢您的评论,Q已经编辑过了。 - Tez Wingfield
显示剩余2条评论
2个回答

3

编辑

本回答涉及网关与微服务之间的通信。当应用程序与网关进行通信时,用户当然应该被正确认证

结束编辑

首先,微服务不应该从互联网可达。它们只能从网关(可以集群)访问。

其次,您需要能够识别当前用户。您可以通过将UserId作为HTTP标头传递来实现。创建一个WebApi过滤器,该过滤器获取该标头并从中创建自定义的IPrincipal

最后,您需要某种方式来确保请求来自网关或另一个微服务。一种简单的方法是在令牌上使用HMAC身份验证。

将密钥存储在每个服务和网关的web.config中。然后只需发送每个请求的令牌(您可以使用WebApi身份验证过滤器进行身份验证)

要生成哈希,请使用.NET中的HMACSHA256类:

private static string CreateToken(string message, string secret)
{
    secret = secret ?? "";
    var keyByte = Encoding.ASCII.GetBytes(secret);
    var messageBytes = Encoding.ASCII.GetBytes(message);
    using (var hasher = new HMACSHA256(keyByte))
    {
        var hashmessage = hasher.ComputeHash(messageBytes);
        return Convert.ToBase64String(hashmessage);
    }
}

所以在你的MicroServiceClient中,你需要这样做:
var hash = CreateToken(userId.ToString(), mySharedSecret);
var myHttpRequest = HttpRequest.Create("yourUrl");
myHttpRequest.AddHeader("UserId", userId);
myHttpRequest.AddHeader("UserIdToken", hash);
//send request..

在微服务中,您可以创建一个过滤器,例如:

public class TokenAuthenticationFilterAttribute : Attribute, IAuthenticationFilter
{
    protected string SharedSecret
    {
        get { return ConfigurationManager.AppSettings["SharedSecret"]; }
    }

    public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        await Task.Run(() =>
        {
            var userId = context.Request.Headers.GetValues("UserId").FirstOrDefault();
            if (userId == null)
            {
                context.ErrorResult = new StatusCodeResult(HttpStatusCode.Forbidden, context.Request);
                return;
            }

            var userIdToken = context.Request.Headers.GetValues("UserIdToken").FirstOrDefault();
            if (userIdToken == null)
            {
                context.ErrorResult = new StatusCodeResult(HttpStatusCode.Forbidden, context.Request);
                return;
            }

            var token = CreateToken(userId, SharedSecret);
            if (token != userIdToken)
            {
                context.ErrorResult = new StatusCodeResult(HttpStatusCode.Forbidden, context.Request);
                return;
            }


            var principal = new GenericPrincipal(new GenericIdentity(userId, "CustomIdentification"),
                new[] {"ServiceRole"});
            context.Principal = principal;
        });
    }

    public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
    }

    public bool AllowMultiple
    {
        get { return false; }
    }

    private static string CreateToken(string message, string secret)
    {
        secret = secret ?? "";
        var keyByte = Encoding.ASCII.GetBytes(secret);
        var messageBytes = Encoding.ASCII.GetBytes(message);
        using (var hasher = new HMACSHA256(keyByte))
        {
            var hashmessage = hasher.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashmessage);
        }
    }
}

当然,让我用 UserId: admin 标头来创建一个请求。 - CodeCaster
@CodeCaster:如果您已经成功访问了网关服务器,难道您不认为您可以通过其他方式对系统造成伤害吗? - jgauffin
网络协议的第一条规则是:不要相信发件人(好吧,至少在 SMTP、FTP 和 HTTP 发明之后……)。OP 正在谈论桌面和移动应用程序与 Web 服务通信。你永远不能相信任何网络没有恶意用户存在,因此你需要每个用户从应用程序中进行身份验证。 - CodeCaster
如果您的主机支持,例如Azure,您可以将API设置为内部,以便仅可由在Azure Services中运行的VM访问。此外,我同意@jgauffin的观点,即微服务必须信任网关,一旦与网关建立了信任关系,您就无需验证它所说的一切。 - Adam
1
@CodeCaster:认证是在应用程序<->网关之间完成的。我的回答是关于网关<->微服务的。 - jgauffin
显示剩余2条评论

3

选项1(优先)

简单的方法是将微服务放在网关后面,因此您可以将服务列入白名单以连接到它们,这意味着只有经过授权和信任的方​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ the gateway 才能获得访问权限。客户端不应直接访问它们。网关就像您的夜店保镖。

选项2

您可以使用JWT或某种形式的令牌,并在服务之间共享密钥。我使用JWT授权承载令牌。

其他服务无需查询用户服务,他们只需要知道令牌是否有效,然后他们就有授权使用API。我从客户端传递JWT到网关并将其注入发送到其后面的其他服务的请求中,直接通过。

后面的微服务需要与网关具有相同的JWT消耗来进行授权,但如我所述,这只是确定有效令牌,而不是查询有效用户。

但是这样做存在一个问题,就是一旦有人获得授权,他们可以调用其他用户数据,除非您在令牌中包含类似声明的内容。

我的想法

从单块到微服务的挑战之一是您需要切换信任的位置。在单块中,您控​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​控​​​​​​​制​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​所有​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​


好的,我完成了编辑。希望这样解释清楚了。 - Adam
感谢您的时间。 - Tez Wingfield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接