基本概念是我使用WebAPI创建登录页面,用户向我发送我处理的凭据,尝试在进行数据库搜索后保留一些信息,使用声明设置授权和IPrincipal然后将用户传回生成的Auth Token。
使用SignalR,我让Javascript在查询字符串中传回授权令牌。我们的想法是匹配以查看Owin上下文是否与您在尝试进行SignalR连接时传递的令牌相匹配。它允许您使用SignalR文档建议的授权属性连接到集线器。
//SignalR
app.Map("/listener", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new QsoAuthBearerProvider()
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.Resolver = GlobalHost.DependencyResolver;
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromMinutes(20);
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
map.RunSignalR(hubConfiguration);
});
public class QsoAuthBearerProvider: OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
if (context.Request.Path.ToString().Contains("connect"))
{
var value = context.Request.Query.Get("authToken");
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}
}
return Task.FromResult<object>(null);
}
}
在这里,我尝试设定人和一切的身份。
namespace WebSocketServer.Controller
{
[RoutePrefix("login")]
public class LoginController : ApiController
{
private static readonly log4net.ILog Log = ...
private string CreateSessionKey()
{
//Creates a token key and passes it back as a string
}
private ClaimsPrincipal SetOwinContext(string sessionKey, UserCreds creds)
{
var userRepo = (User repository);
var trackingRepo = (Tracking Repository);
try
{
bool authenticated = false;
authenticated = //Check with 3rd party if creds are good
if (authenticated)
{
using (IDbConnection con = new SqlConnection(Constants.CONN_STR))
{
var builder = new SqlBuilder();
var query = //My query for checking things using Dapper
var user = con.Query<SystemUser>(query.RawSql, query.Parameters).First();
if (user == null || (user.LockedOut != false && user.LockedOut != null)) return null;
userRepo.SuccessfulLogin(creds.Username);
var owinContext = HttpContext.Current.GetOwinContext();
var claims = new List<Claim>
{
//Name
new Claim(ClaimTypes.Name, sessionKey),
//Username
new Claim(ClaimTypes.NameIdentifier, creds.Username??""),
new Claim(ClaimTypes.Actor,user.LastName + ", " + user.FirstName),
//SystemId in Db
new Claim(ClaimTypes.PrimarySid, user.SystemUserId.ToString() ??""),
//ConnectionId
//new Claim(ClaimTypes.Sid, " "),
//Authenticated
new Claim(ClaimTypes.Authentication, "true"),
//Token
new Claim(ClaimTypes.Thumbprint, sessionKey),
//Machine Ip
new Claim(ClaimTypes.WindowsDeviceClaim, creds.MachineIp ?? ""),
//Default View
new Claim(ClaimTypes.GroupSid, user.DefaultViewId?.ToString() ?? ""),
//Role
new Claim(ClaimTypes.Role, (user.IsAdmin != null && user.IsAdmin == true) ? "Admin" : "")
};
var claimsIdentity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
Thread.CurrentPrincipal = claimsPrincipal;
owinContext.Authentication.User = claimsPrincipal;
var tracking = new ConnectionTracking
{
Authenticated = true,
SystemUserId = user.SystemUserId,
Token = sessionKey,
MachineIp = creds.MachineIp
};
trackingRepo.Add(tracking);
trackingRepo.Save();
return claimsPrincipal;
}
}
return null;
}
catch (Exception ex)
{
return null;
}
finally
{
trackingRepo.Dispose();
userRepo.Dispose();
}
}
// POST api/<controller>
/// <summary>
/// Login registration
/// </summary>
/// <param name="userCreds">String containing user name and password</param>
/// <returns></returns>
//[ActionName("register")]
public JObject Post([FromBody]UserCreds creds)
{
try
{
var privatePrincial = SetOwinContext(CreateSessionKey(), creds);
var principal = HttpContext.Current.GetOwinContext();
if (principal.Authentication.User != null)
{
var claim = principal.Authentication.User.FindAll(o => o.Type == ClaimTypes.Thumbprint)
.First<Claim>();
Thread.CurrentPrincipal = HttpContext.Current.GetOwinContext().Authentication.User;
return JObject.FromObject(new
{
token = claim.Value
});
}
else
{
return JObject.FromObject(new { token = "-1" });
}
}
catch (Exception ex)
{
Log.Fatal(ex.ToString());
return JObject.FromObject(new { error = ex.ToString() });
}
}
}
}
namespace WebSocketServer.Authentication
{
/// <summary>
/// Custom hub authorization
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class HubAuthorizeAttribute : AuthorizeAttribute
{
private static readonly log4net.ILog Log = ...
private string _ConnId { get; set; }
/// <summary>
/// Invocation for hub; Gets the connection string id
/// </summary>
/// <param name="hubIncomingInvokerContext"><see cref="IHubIncomingInvokerContext"/></param>
/// <param name="appliesToMethod"></param>
/// <returns>Boolean</returns>
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
try
{
var principal = HttpContext.Current.GetOwinContext();
//hubIncomingInvokerContext.Hub.Context.QueryString.Get("authToken");
if (principal != null && principal.Authentication.User.Identity.IsAuthenticated == true)
{
this._ConnId = hubIncomingInvokerContext.Hub.Context.ConnectionId;
return true;
}
return false;
}
catch (Exception ex)
{
Log.Fatal(ex.ToString());
return false;
}
}
protected override bool UserAuthorized(IPrincipal user)
{
if (user == null) return false;
var userPrincipal = user as IUserPrincipal;
return userPrincipal != null && userPrincipal.Authenticated;
}
/// <summary>
/// Authorization function for hub, works only if principal authenticated is true
/// </summary>
/// <param name="hubDescriptor"><see cref="HubDescriptor"/></param>
/// <param name="request"></param>
/// <returns>Boolean if person can access hub or not</returns>
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
try
{
var principal = HttpContext.Current.User as IUserPrincipal;
return principal != null && principal.Authenticated == true;
//return true;
}
catch (Exception ex)
{
Log.Fatal(ex.ToString());
return false;
}
}
}
}
所以发生的事情是在我的集线器中使用[HubAuthorize]属性并尝试访问当前上下文。现在,Request上下文始终将User作为null,与Thread.CurrentPrincipal,HttpContext.Current.GetOwinContext()相同。因此,似乎在使用WebAPI发回之后,上下文似乎丢失了。
我不知道如何在Owin,我可以搜索身份或校长的不同实例,所以我可以查看那里的声明。
我知道我的SignalR按原样工作,顶部没有任何属性。我的代码在协商和连接时似乎没有干涉。我确实看到我的authToken的查询字符串涉及被传递,我能够在我的QsoAuthBearerProvider.RequestToken()函数中看到它。在上下文中,我可以看到它在那里,但不保留任何用户信息。
我们将Angular作为纯前端,而SignalR在其自己的运行站点上充当中间件。
非常感谢您在此时提供的任何帮助。我已经在这几天敲了敲头,但收效甚微。
谢谢!