asp.net – .NET Core WebAPI OpenIdDict(凭据流)和Angular2客户端:401成功登录后(完全重新登录)

前端之家收集整理的这篇文章主要介绍了asp.net – .NET Core WebAPI OpenIdDict(凭据流)和Angular2客户端:401成功登录后(完全重新登录)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试使用OpenIdDict创建一个消耗.NET Core Web API的Angular2 SPA,其中包含凭据流.在为这个问题创建一个repro解决方案时,我还详细介绍了自述文件中的所有步骤,所以希望这篇文章对像我这样的新手有用.请在这些存储库中找到完整的repro解决方案:

>服务器端(.NET Core OpenIdDict),包含构建您自己的详细说明:https://github.com/Myrmex/repro-oidang
>客户端(Angular2):https://github.com/Myrmex/repro-angoid

至于服务器端,我按照OpenIdDict提供的关于此流程的示例(https://github.com/openiddict/openiddict-samples/blob/master/samples/PasswordFlow).以下是Startup中最相关的位:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddCors();
  4.  
  5. services.AddEntityFrameworksqlServer()
  6. .AddDbContext<CatalogContext>(options =>
  7. options.UsesqlServer(Configuration.GetConnectionString("Catalog")))
  8. .AddDbContext<ApplicationDbContext>(options =>
  9. options.UsesqlServer(Configuration.GetConnectionString("Catalog")));
  10.  
  11. services.AddIdentity<ApplicationUser,ApplicationRole>()
  12. .AddEntityFrameworkStores<ApplicationDbContext>()
  13. .AddDefaultTokenProviders();
  14.  
  15. services.AddOpenIddict<ApplicationDbContext>()
  16. .DisableHttpsRequirement()
  17. .EnableTokenEndpoint("/connect/token")
  18. .EnablelogoutEndpoint("/connect/logout")
  19. .EnableUserinfoEndpoint("/connect/userinfo")
  20. .AllowPasswordFlow()
  21. .AllowRefreshTokenFlow()
  22. .AddEphemeralSigningKey();
  23.  
  24. services.AddMvc()
  25. .AddJsonOptions(options =>
  26. {
  27. options.SerializerSettings.ContractResolver =
  28. new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
  29. });
  30.  
  31. // add my services
  32. // ...
  33.  
  34. services.AddTransient<IDatabaseInitializer,DatabaseInitializer>();
  35. services.AddSwaggerGen();
  36. }
  37.  
  38. public void Configure(IApplicationBuilder app,IHostingEnvironment env,ILoggerFactory loggerFactory,IDatabaseInitializer databaseInitializer)
  39. {
  40. loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  41. loggerFactory.AddDebug();
  42. loggerFactory.AddNLog();
  43.  
  44. app.UseDefaultFiles();
  45. app.UseStaticFiles();
  46.  
  47. app.UseCors(builder =>
  48. builder.WithOrigins("http://localhost:4200")
  49. .AllowAnyHeader()
  50. .AllowAnyMethod());
  51.  
  52. app.USEOAuthValidation();
  53. app.USEOpenIddict();
  54. app.UseMvc();
  55. databaseInitializer.Seed().GetAwaiter().GetResult();
  56. env.ConfigureNLog("nlog.config");
  57. app.UseSwagger();
  58. app.UseSwaggerUi();
  59. }

如果我用Fiddler测试它,它工作正常:令牌请求获取令牌,然后我可以将它包含在Authorization标头中以访问任何受保护的API,它按预期返回JSON数据.

示例令牌请求:

  1. POST http://localhost:51346/connect/token
  2. Content-Type: application/x-www-form-urlencoded
  3.  
  4. grant_type=password&scope=offline_access profile email roles&resource=http://localhost:4200&username=...&password=...

样本资源请求:

  1. GET http://localhost:51346/api/values
  2. Content-Type: application/json
  3. Authorization: Bearer ...received token here...

然而,在客户端,每当我尝试相同的请求时,我都会收到401错误;看着日志,似乎Angular2 Http服务根本没有发送所需的头,因为我收到错误身份验证被跳过,因为没有收到持有者令牌(请参阅下面的更多日志条目).

检索一些资源的服务是这样的:

  1. import { Injectable } from '@angular/core';
  2. import { Http,Response } from '@angular/http';
  3. import { Observable } from 'rxjs/Observable';
  4. import { SettingsService } from './settings.service';
  5. import { AuthenticationService } from './authentication.service';
  6.  
  7. export interface ICategory {
  8. id: string;
  9. name: string;
  10. }
  11.  
  12. @Injectable()
  13. export class CategoryService {
  14.  
  15. constructor(
  16. private _http: Http,private _settings: SettingsService,private _authService: AuthenticationService) { }
  17.  
  18. public getCategories(): Observable<ICategory[]> {
  19. let url = this._settings.apiBaseUrl + 'categories';
  20. let options = {
  21. headers: this._authService.createAuthHeaders({
  22. 'Content-Type': 'application/json'
  23. })
  24. };
  25. return this._http.get(url,options).map((res: Response) => res.json())
  26. .catch((error: any) => Observable.throw(error.json().error || 'server error'));
  27. }
  28. }

帮助程序createAuthHeaders只获取表示Header(https://angular.io/docs/ts/latest/api/http/index/Headers-class.html)条目的某些属性,检索存储的标记,将Authentication条目附加到标题,然后返回:

  1. public createAuthHeaders(headers?: { [name: string]: any }): Headers {
  2. let auth = new Headers();
  3. if (headers) {
  4. for (let key in headers) {
  5. if (headers.hasOwnProperty(key)) {
  6. auth.append(key,headers[key]);
  7. }
  8. }
  9. }
  10. let tokenResult = this._localStorage.retrieve(this._settings.tokenStorageKey,true);
  11. if (tokenResult) {
  12. auth.append('Authentication','Bearer ' + tokenResult.access_token);
  13. }
  14. return auth;
  15. }

然而,当尝试将响应映射到JSON对象(JSON输入的意外结束)时,此请求获得401响应然后Angular抛出.

我必须补充说,一旦客户端获得令牌,它就会发出另一个请求,以检索用户信息,这样可以正常工作;这是它(获取用户信息后的代码):

  1. public login(name: string,password: string) {
  2. let body = 'grant_type=password&scope=offline_access profile email roles' +
  3. `&resource=${this._settings.appBaseUrl}&username=${name}&password=${password}`;
  4.  
  5. this._http.post(
  6. this._settings.authBaseUrl + `token`,body,{
  7. headers: new Headers({
  8. 'Content-Type': 'application/x-www-form-urlencoded'
  9. })
  10. }).map(res => res.json())
  11. .subscribe(
  12. (token: ITokenResult) => {
  13. if (token.expires_in) {
  14. token.expires_on = this.calculateExpirationDate(+token.expires_in);
  15. }
  16. this._localStorage.store(this._settings.tokenStorageKey,token,true);
  17. // get user info
  18. this._http.get(this._settings.authBaseUrl + 'userinfo',{
  19. headers: new Headers({
  20. 'Content-Type': 'application/json','Authorization': 'Bearer ' + token.access_token
  21. })
  22. }).map(res => res.json())
  23. .subscribe((info: IUserInfoResult) => {
  24. let user: IUser = {
  25. id: info.name,email: info.email,name: info.name,firstName: info.given_name,lastName: info.family_name,role: info.role,verified: info.email_verified
  26. };
  27. this._localStorage.store(this._settings.userStorageKey,user,true);
  28. this.userChanged.emit(user);
  29. },error => {
  30. console.log(error);
  31. });
  32. },error => {
  33. console.log(error);
  34. });
  35. }

但是,使用上述服务构建的任何其他请求都会失败.使用引用函数构建的标题有什么问题?

以下是服务器端的一些日志条目:

  1. 2016-11-18 20:41:31.9815|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
  2. 2016-11-18 20:41:31.9815|0|OpenIddict.Infrastructure.OpenIddictProvider|INFO| The token request validation process was skipped because the client_id parameter was missing or empty.
  3. 2016-11-18 20:41:32.0715|0|AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware|INFO| No explicit audience was associated with the access token.
  4. 2016-11-18 20:41:32.1165|10|AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware|INFO| AuthenticationScheme: ASOS signed in.
  5. 2016-11-18 20:41:32.1635|3|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Bearer.
  6. 2016-11-18 20:41:57.7430|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
  7. 2016-11-18 20:41:57.7430|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
  8. 2016-11-18 20:41:57.8820|12|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| AuthenticationScheme: Bearer was challenged.
  9. 2016-11-18 20:41:57.9305|12|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| AuthenticationScheme: Bearer was challenged.
  10. 2016-11-18 20:41:57.9465|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
  11. 2016-11-18 20:41:57.9925|12|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| AuthenticationScheme: Bearer was challenged.

解决方法

您的不记名令牌使用不正确.
  1. auth.append('Authentication','Bearer ' + tokenResult.access_token) // wrong
  2. auth.append('Authorization','Bearer ' + tokenResult.access_token) // right

标题需要是授权.见https://tools.ietf.org/html/rfc6750#section-2.1

猜你在找的.NET Core相关文章