过去几天我一直在玩微服务模式,一切进展顺利,但安全性似乎让我感到困惑。
<击>
所以如果我可以问一个问题:
如何处理单个服务的用户身份验证?目前我将请求传递给Gateway API
,后者又连接到服务。
击>
编辑问题请见下文
请记住,个别服务不应该彼此了解。 Gateway
就是聚合器。
当前架构。
用于模拟请求的小代码:
前端 - 客户端应用
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;
}
我使用泛型来确定它一旦点击网关就需要触发的位置。
因此,如果Type
类别,它将触发类别服务,从而调用:
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
并确定用户是否经过身份验证 - 一切顺利但现在我想从用户收到的Message Service
中获取所有消息。如果用户通过身份验证,我无法在网关中说出,因为这无法解决在Message Service
Gateway API
的问题
我也无法为每个单独的服务添加身份验证,因为这需要所有相应的服务与User Service
通信,这会破坏模式的目的。
修正: 仅允许网关呼叫服务。应阻止对网关外部服务的请求。
我知道安全是一个广泛的主题,但在目前的背景下,我希望有人可以指导我采取最佳行动来解决问题。
目前,我已经在应用程序的所有应用程序中硬编码Guid
,如果应用程序相同,则会提取数据。
答案 0 :(得分:3)
<强> 修改 强>
这个答案是关于Gateway&lt; - &gt;微服务通信。当应用程序与网关
进行通信时,用户当然应该进行适当的身份验证结束编辑
首先,微服务不应该从互联网上获得。它们只能从网关(可以聚集)访问。
其次,您需要能够识别当前用户。您可以通过将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);
}
}
}
答案 1 :(得分:2)
选项1(首选)
简单的方法是微服务应该位于网关之后,因此您可以将服务列入白名单以连接到它们,这意味着只有授权和信任方才能访问(即仅限网关)。客户不应该直接访问它们。 Gateway是您的夜总会保镖。
选项2
您可以使用JWT或某种形式的令牌,并在服务之间共享密钥。我使用JWT授权承载令牌。
其他服务不需要查询用户服务,他们只需要知道令牌有效,然后他们就有权使用API。我将JWT从客户端传递到网关并将其注入到后面发送给其他服务的请求中,直接通过。
后面的微服务需要与授权网关具有相同的JWT消耗,但正如我所提到的那样只是确定有效令牌,而不是查询有效用户。
但是这有一个问题,一旦某人获得授权,他们就可以跳过其他用户数据的呼叫,除非你在令牌中包含类似索赔的内容。
我的想法
我发现从Monolithic到Micro Services的挑战是你需要切换到你信任的地方。在Monolithic中,您可以控制所有负责人。微服务的关键是其他服务完全控制其域。您必须信任其他服务以履行其义务,并且不希望在超出必要范围的每个级别重新检查和重新授权。