从AngularJs http web api请求重定向到Identity Server登录页面
|
我正在尝试从Angular的$http服务调用API控制器方法时重定向到Identity Server的默认登录页面.
我的Web项目和Identity Server位于不同的项目中,并具有不同的Startup.cs文件. Web项目Statup.cs如下 public class Startup
{
public void Configuration(IAppBuilder app)
{
AntiForgeryConfig.UniqueClaimTypeIdentifier = Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string,string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",});
var openIdConfig = new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44301/identity",ClientId = "baseballStats",Scope = "openid profile roles baseballStatsApi",RedirectUri = "https://localhost:44300/",ResponseType = "id_token token",SignInAsAuthenticationType = "Cookies",UseTokenLifetime = false,Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo"),n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
// create new identity and set name and role claim type
var nid = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,Thinktecture.IdentityServer.Core.Constants.ClaimTypes.GivenName,Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Role);
userInfo.Claims.ToList().ForEach(c => nid.AddClaim(new Claim(c.Item1,c.Item2)));
// keep the id_token for logout
nid.AddClaim(new Claim("id_token",n.ProtocolMessage.IdToken));
// add access token for sample API
nid.AddClaim(new Claim("access_token",n.ProtocolMessage.AccessToken));
// keep track of access token expiration
nid.AddClaim(new Claim("expires_at",DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));
// add some other app specific claim
nid.AddClaim(new Claim("app_specific","some data"));
n.AuthenticationTicket = new AuthenticationTicket(
nid,n.AuthenticationTicket.Properties);
n.Request.Headers.SetValues("Authorization ",new string[] { "Bearer ",n.ProtocolMessage.AccessToken });
}
}
};
app.USEOpenIdConnectAuthentication(openIdConfig);
app.UseResourceAuthorization(new AuthorizationManager());
app.Map("/api",inner =>
{
var bearerTokenOptions = new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44301/identity",RequiredScopes = new[] { "baseballStatsApi" }
};
inner.UseIdentityServerBearerTokenAuthentication(bearerTokenOptions);
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
inner.UseWebApi(config);
});
}
}
您会注意到API使用不记名令牌身份验证进行保护,而应用程序的其余部分使用OpenIdConnect. Identity Server Startup.cs类是 public class Startup
{
public void Configuration(IAppBuilder app)
{
var policy = new System.Web.Cors.CorsPolicy
{
AllowAnyOrigin = true,AllowAnyHeader = true,AllowAnyMethod = true,SupportsCredentials = true
};
policy.ExposedHeaders.Add("Location");
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
});
app.Map("/identity",idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",SigningCertificate = LoadCertificate(),Factory = InMemoryFactory.Create(
users: Users.Get(),clients: Clients.Get(),scopes: Scopes.Get())
});
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(@"{0}binConfigurationidsrv3test.pfx",AppDomain.CurrentDomain.BaseDirectory),"idsrv3test");
}
}
请注意,我添加了一个CorsPolicy条目,以便允许Web App重定向到Login页面.此外,Cors策略公开了Location请求标头,因为它包含我要重定向到的url. Web Api控制器方法使用授权属性进行保护,就像这样 [HttpPost]
[EnableCors(origins: "*",headers: "*",methods: "*")]
[Authorize]
public PlayerData GetFilteredPlayers(PlayerInformationParameters parameters)
{
var playerInformation = composer.Compose<PlayerInformation>().UsingParameters(parameters);
var players = playerInformation.Players
.Select(p => new {
p.NameLast,p.NameFirst,p.Nickname,p.BirthCity,p.BirthState,p.BirthCountry,p.BirthDay,p.BirthMonth,p.BirthYear,p.Weight,p.Height,p.College,p.Bats,p.Throws,p.Debut,p.FinalGame
});
var playerData = new PlayerData { Players = players,Count = playerInformation.Count,Headers = GetHeaders(players) };
return playerData;
}
角度工厂调用$http,如下所示 baseballApp.factory('playerService',function ($http,$q) {
return {
getPlayerList: function (queryParameters) {
var deferred = $q.defer();
$http.post('api/pitchingstats/GetFilteredPlayers',{
skip: queryParameters.skip,take: queryParameters.take,orderby: queryParameters.orderby,sortdirection: queryParameters.sortdirection,filter: queryParameters.filter
}).success(function (data,status) {
deferred.resolve(data);
}).error(function (data,status) {
deferred.reject(status);
});
return deferred.promise;
}
}});
发生此调用时,响应状态为200,并在数据中返回登录页面的html. 此外,我可以在Chrome的“网络”标签上看到该响应的“位置”标题包含“登录”页面的网址.但是,如果我设置了一个http拦截器,我只看到Accept头已经传递给了javascript. 以下是Chrome网络标签中显示的http标头: 由于某种原因,响应没有Access-Control-Allow-Origin标头. 所以我有以下问题: 有没有办法可以在角度客户端代码中访问响应的Location标头以重定向到它? 为了知道存在身份验证错误,我怎样才能让服务器向我发送401而不是200? 有没有更好的方法来做到这一点,如果是这样,怎么样? 谢谢你的帮助! 编辑: 我添加了一个自定义AuthorizeAttribute来确定从过滤器返回的http状态代码. 自定义过滤器代码 public class BearerTokenAutorizeAttribute : AuthorizeAttribute
{
private const string AjaxHeaderKey = "X-Requested-With";
private const string AjaxHeaderValue = "XMLHttpRequest";
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var headers = actionContext.Request.Headers;
if(IsAjaxRequest(headers))
{
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
actionContext.Response.StatusCode = System.Net.HttpStatusCode.Forbidden;
else
actionContext.Response.StatusCode = System.Net.HttpStatusCode.Unauthorized;
}
base.HandleUnauthorizedRequest(actionContext);
var finalStatus = actionContext.Response.StatusCode;
}
private bool IsAjaxRequest(HttpRequestHeaders requestHeaders)
{
return requestHeaders.Contains(AjaxHeaderKey) && requestHeaders.GetValues(AjaxHeaderKey).FirstOrDefault() == AjaxHeaderValue;
}
我已经观察到两件事:首先,X-Requested-With标头不包含在客户端$http服务生成的请求中.此外,基本方法返回的最终http状态为401 – 未授权.这意味着状态代码在链的某个地方发生了变化. 请不要觉得你必须回答所有问题.任何帮助将不胜感激! 解决方法
您可能已经正确配置了服务器
登录页面html作为对$http调用的响应 – >它是 应该这样工作: angularjs $http
你得到200 OK响应,因为这是最后的响应,因为重定向立即被跟踪,它的结果被解析为$http服务结果,响应头也是最终响应 实现所需结果的一种方法 – 浏览器重定向到登录页面: web api控制器api / pitchingstats / GetFilteredPlayer可以返回包含{redirectUrl:’登录页面’}字段的json有效负载的错误响应(401),而不是重定向请求服务器端(从Web项目到Identity Server)或者可以读作response.headers(‘x-redirect-url’)的标题 通常可以在$httpInterceptors中配置类似的逻辑来处理未经授权的访问响应并将它们重定向到登录页面 – 重定向在客户端进行管理 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
