.net core 使用adaljs将请求从spa发送到.netcore web api始终返回401
我有一个spa正在向web api发送get请求 当我没有authorize属性时,我可以获得值(当然!)。添加authorize属性总是返回401响应 在我连续两周对此感到困惑之后,我觉得只有上帝才能帮助我 我有以下问题:.net core 使用adaljs将请求从spa发送到.netcore web api始终返回401,.net-core,asp.net-core-webapi,adal.js,.net Core,Asp.net Core Webapi,Adal.js,我有一个spa正在向web api发送get请求 当我没有authorize属性时,我可以获得值(当然!)。添加authorize属性总是返回401响应 在我连续两周对此感到困惑之后,我觉得只有上帝才能帮助我 我有以下问题: 我做错了什么 有更好的方法吗 如何在服务器端记录传入令牌? (这样我就可以在jwt.io上验证它) 假设我的钥匙、租户、客户(id)等设置正确 我的spa代码如下: 'use strict'; angular.module('todoApp') .controlle
'use strict';
angular.module('todoApp')
.controller('homeCtrl', ['$scope', '$http', 'adalAuthenticationService', '$location', function ($scope, $http, adalService, $location) {
$scope.apiData = [];
$scope.login = function () {
adalService.login().then(function () {
console.log('yay');
});
};
$scope.logout = function () {
adalService.logOut();
};
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
$scope.getData = function () {
// #1: Set up ADAL
var authContext = new AuthenticationContext({
clientId: 'myclientid',
postLogoutRedirectUri: window.location
});
var user = authContext.getCachedUser();
if (user) {
console.log(user);
console.log('Signed in as: ' + user.userName);
} else {
console.log('Not signed in');
}
var tokenStored;
authContext.acquireToken(
'https://graph.windows.net',
function (error, token) {
// TODO: Handle error obtaining access token
if (error || !token) {
console.log('Error no token');
return;
}
console.log("token is:" + token);
tokenStored = token;
$http.get('https://localhost:44301/api/values', {
headers: { 'Authorization': 'Bearer ' + tokenStored, }
}).then(function (response) {
$scope.apiData = response.data;
console.log(response);
alert('Data recieved');
});
});
};
}]);
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//ToDo: Implement Logger Factory
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Shows UseCors with CorsPolicyBuilder.
// global policy - assign here or on each controller
app.UseCors("CorsPolicy");
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
// TokenValidationParameters = tokenValidationParameters
});
app.UseMvc();
}
我的Api Startup.cs如下所示:
'use strict';
angular.module('todoApp')
.controller('homeCtrl', ['$scope', '$http', 'adalAuthenticationService', '$location', function ($scope, $http, adalService, $location) {
$scope.apiData = [];
$scope.login = function () {
adalService.login().then(function () {
console.log('yay');
});
};
$scope.logout = function () {
adalService.logOut();
};
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
$scope.getData = function () {
// #1: Set up ADAL
var authContext = new AuthenticationContext({
clientId: 'myclientid',
postLogoutRedirectUri: window.location
});
var user = authContext.getCachedUser();
if (user) {
console.log(user);
console.log('Signed in as: ' + user.userName);
} else {
console.log('Not signed in');
}
var tokenStored;
authContext.acquireToken(
'https://graph.windows.net',
function (error, token) {
// TODO: Handle error obtaining access token
if (error || !token) {
console.log('Error no token');
return;
}
console.log("token is:" + token);
tokenStored = token;
$http.get('https://localhost:44301/api/values', {
headers: { 'Authorization': 'Bearer ' + tokenStored, }
}).then(function (response) {
$scope.apiData = response.data;
console.log(response);
alert('Data recieved');
});
});
};
}]);
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//ToDo: Implement Logger Factory
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Shows UseCors with CorsPolicyBuilder.
// global policy - assign here or on each controller
app.UseCors("CorsPolicy");
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
// TokenValidationParameters = tokenValidationParameters
});
app.UseMvc();
}
我的控制器方法如下所示
[Route("api/values")]
[Authorize]
[EnableCors("CorsPolicy")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IActionResult Get()
{
if (!HttpContext.User.Identity.IsAuthenticated)
{
var results = _interconnectCodesRepository.GetCodes();
return Ok(results);
}
else
{
return BadRequest();
}
}
}
}
如有任何建议或提示,将不胜感激
感谢从.net core web API项目获取令牌,我们可以添加
AuthenticationFailed
事件,如下所示:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAD:Tenant"]),
Audience = Configuration["AzureAd:Audience"],
Events = new JwtBearerEvents
{
OnAuthenticationFailed= AuthenticationFailed
}
});
private Task AuthenticationFailed(AuthenticationFailedContext authenticationFailedContext)
{
Debug.WriteLine(authenticationFailedContext.Request.Headers["authorization"]);
return Task.FromResult(0);
}
您正在使用resroucehttps://graph.windows.net
中的代码用于Azure Graph REST,而不是您的API。无需在SPA应用程序的客户端中手动获取令牌。ADAL库将根据资源自动获取并附加令牌。我们只需要初始化要请求的端点。以下是js代码供您参考:
var myApp = angular.module('myApp', ['AdalAngular']).config(['$httpProvider', 'adalAuthenticationServiceProvider', function ($httpProvider, adalProvider) {
//{Array} endpoints - Collection of {Endpoint-ResourceId} used for automatically attaching tokens in webApi calls.
var endpoints = {
"https://localhost:44327/": "https://adfei.onmicrosoft.com/ToGoAPI",
};
adalProvider.init(
{
instance: 'https://login.microsoftonline.com/',
tenant: 'adfei.onmicrosoft.com',
clientId: 'e2354bba-e915-4cb8-a48d-bcda101b8603',
extraQueryParameter: 'nux=1',
endpoints: endpoints,
},
$httpProvider
);
}])
myApp.controller('homeCtrl', ['$scope', '$http', 'adalAuthenticationService', '$location', 'toGoListSvc', function ($scope, $http, adalService, $location, toGoListSvc) {
$scope.double = function (value) { return value * 2; };
$scope.login = function () {
adalService.login();
};
$scope.logout = function () {
adalService.logOut();
};
$scope.getData = function () {
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
$http.get('https://localhost:44327/api/ToGoList').success(function (results) {
console.log(results)
$scope.toGoList = results;
});
}
}]);
对于web API方面,我们需要指定权限
和受众
或您想要的其他参数(请参阅第一段代码)
在Azure方面,我们需要注册两个web应用程序。一个表示客户端,另一个表示受Azure AD保护的资源。例如,在我的测试场景中,我注册了ToOSPA和ToGoAPI并授予权限,如下图所示:
为了使ToDoSPA应用程序与Azure AD集成,并为SPA应用程序提供隐式流,我们还需要修改其清单,将oauth2AllowImplicitFlow
设置为true
此外,以下是一些关于使用Azure AD保护web API的有用链接:
更新(自定义AudienceValidator)
//将应用程序配置为使用Jwt承载身份验证
应用程序UseJWTBeareAuthentication(新JWTBeareOptions
{
TokenValidationParameters=新的Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
AudienceValidator=(受众、securityToken、validationParameters)=>
{
字符串[]AllowedAudits={”https://adfei.onmicrosoft.com/TodoListService", "https://graph.windows.net" };
return allowedaccountries.Contains(accountries.First());
},
},
自动验证=真,
自动挑战=正确,
Authority=String.Format(配置[“AzureAd:AadInstance”]、配置[“AzureAd:Tenant”]),
//观众=配置[“AzureAd:观众”],
});
要从.net core web API项目获取令牌,我们可以添加AuthenticationFailed
事件,如下所示:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAD:Tenant"]),
Audience = Configuration["AzureAd:Audience"],
Events = new JwtBearerEvents
{
OnAuthenticationFailed= AuthenticationFailed
}
});
private Task AuthenticationFailed(AuthenticationFailedContext authenticationFailedContext)
{
Debug.WriteLine(authenticationFailedContext.Request.Headers["authorization"]);
return Task.FromResult(0);
}
您正在使用resroucehttps://graph.windows.net
中的代码用于Azure Graph REST,而不是您的API。无需在SPA应用程序的客户端中手动获取令牌。ADAL库将根据资源自动获取并附加令牌。我们只需要初始化要请求的端点。以下是js代码供您参考:
var myApp = angular.module('myApp', ['AdalAngular']).config(['$httpProvider', 'adalAuthenticationServiceProvider', function ($httpProvider, adalProvider) {
//{Array} endpoints - Collection of {Endpoint-ResourceId} used for automatically attaching tokens in webApi calls.
var endpoints = {
"https://localhost:44327/": "https://adfei.onmicrosoft.com/ToGoAPI",
};
adalProvider.init(
{
instance: 'https://login.microsoftonline.com/',
tenant: 'adfei.onmicrosoft.com',
clientId: 'e2354bba-e915-4cb8-a48d-bcda101b8603',
extraQueryParameter: 'nux=1',
endpoints: endpoints,
},
$httpProvider
);
}])
myApp.controller('homeCtrl', ['$scope', '$http', 'adalAuthenticationService', '$location', 'toGoListSvc', function ($scope, $http, adalService, $location, toGoListSvc) {
$scope.double = function (value) { return value * 2; };
$scope.login = function () {
adalService.login();
};
$scope.logout = function () {
adalService.logOut();
};
$scope.getData = function () {
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
$http.get('https://localhost:44327/api/ToGoList').success(function (results) {
console.log(results)
$scope.toGoList = results;
});
}
}]);
对于web API方面,我们需要指定权限
和受众
或您想要的其他参数(请参阅第一段代码)
在Azure方面,我们需要注册两个web应用程序。一个表示客户端,另一个表示受Azure AD保护的资源。例如,在我的测试场景中,我注册了ToOSPA和ToGoAPI并授予权限,如下图所示:
为了使ToDoSPA应用程序与Azure AD集成,并为SPA应用程序提供隐式流,我们还需要修改其清单,将oauth2AllowImplicitFlow
设置为true
此外,以下是一些关于使用Azure AD保护web API的有用链接:
更新(自定义AudienceValidator)
//将应用程序配置为使用Jwt承载身份验证
应用程序UseJWTBeareAuthentication(新JWTBeareOptions
{
TokenValidationParameters=新的Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
AudienceValidator=(受众、securityToken、validationParameters)=>
{
字符串[]AllowedAudits={”https://adfei.onmicrosoft.com/TodoListService", "https://graph.windows.net" };
return allowedaccountries.Contains(accountries.First());
},
},
自动验证=真,
自动挑战=正确,
Authority=String.Format(配置[“AzureAd:AadInstance”]、配置[“AzureAd:Tenant”]),
//观众=配置[“AzureAd:观众”],
});
感谢您给出如此清晰简洁的回答。在做出建议的更改之后。我在调试控制台上没有看到任何错误(我以前看到过)。但是,在我的SPA上,我在控制台上收到此错误错误错误:AADSTS65001:用户或管理员未同意使用ID为“a77c3345-3962-40cf-8518-81baac0ace43”的应用程序。为此用户和资源发送交互式授权请求。跟踪ID:94c920d0-68e3-49aa-9a17-4d9d84314631相关ID:e6b165f4-b069-4815-905b-47a3ad9f8960时间戳:2016-12-29 16:04:23ZI如果您正在开发一个单一租户应用程序并通过Azure门户注册该应用程序,则无需向该应用程序提供许可,因为我们在注册时已经提供了许可。在另一种情况下,我们需要先授予应用程序权限。我们可以在获得管理员同意的情况下授予组织权限。发起广告