簡體   English   中英

即使使用 ASP.NET CORE 登錄,我在 Angular 中也有 401 Unauthorized

[英]I have a 401 Unauthorized in Angular even when login whit ASP.NET CORE

我已經在 Angular 中創建了一個登錄名。 我可以在登錄時登錄我得到一個 jwt 密鑰。 我的問題是當我登錄時,我應該能夠從我的 api 中獲取客戶列表。 我正在遵循此處的說明https://code-maze.com/authentication-aspnetcore-jwt-2/我不明白為什么當我嘗試從我的 api 獲取客戶端時會收到 401。

這是相關的角度文件

我有 auth-guard.service.ts

    import { Injectable } from '@angular/core';
    import { CanActivate, Router } from '@angular/router';
    import { JwtHelperService } from '@auth0/angular-jwt';
    
    @Injectable({
      providedIn: 'root'
    })
    export class AuthGuard implements CanActivate {
    
      constructor(private router: Router, private jwtHelper: JwtHelperService) {}
    
    
      canActivate() {
        const token = localStorage.getItem("jwt");
    
        if (token && !this.jwtHelper.isTokenExpired(token)) {
          return true;
        }
        this.router.navigate(["login"]);
        return false;
      }
 }

登錄組件工作

@Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) 導出類 LoginComponent { invalidLogin: boolean;

  constructor(private router: Router, private http: HttpClient) { }

  login(form: NgForm) {
    const credentials = JSON.stringify(form.value);
    this.http.post("https://localhost:44363/api/auth/login", credentials, {
      headers: new HttpHeaders({
        "Content-Type": "application/json"
      })
    }).subscribe(response => {
      const token = (<any>response).token;
      localStorage.setItem("jwt", token);
      this.invalidLogin = false;
      this.router.navigate(["/"]);
    }, err => {
      this.invalidLogin = true;
    });
  } 
}

這是我的 app.module

export function tokenGetter() {
  return localStorage.getItem("jwt");
}


@NgModule({
  declarations: [
    AppComponent,
    NavMenuComponent,
    HomeComponent,
    CounterComponent,
    FetchDataComponent,
    LocationManagerComponent,
    ClientsComponent,
    ClientDetailsComponent,
    LoginComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    DataTablesModule,
    HttpClientModule,
    FormsModule,
    RouterModule.forRoot([
      { path: '', component: HomeComponent, pathMatch: 'full' },
      { path: 'counter', component: CounterComponent },
      { path: 'fetch-data', component: FetchDataComponent },
      { path: 'location-manager', component: LocationManagerComponent },
      { path: 'clients', component: ClientsComponent, canActivate: [AuthGuard]  },
      { path: 'clients/:clientId', component: ClientDetailsComponent, canActivate: [AuthGuard] },
      { path: 'login', component: LoginComponent },
    ]),
    BrowserAnimationsModule,
    JwtModule.forRoot({
      config: {
        tokenGetter: tokenGetter,
        whitelistedDomains: ["localhost:44363"],
        blacklistedRoutes: []
      }
    })
  ],
  providers: [AuthGuard],
  bootstrap: [AppComponent]
})
export class AppModule {}

最后這里是導致我的問題的文件。 當我嘗試從我的 api 獲取客戶端時,我的瀏覽器中出現 401。

@Component({
  selector: 'app-clients',
  templateUrl: './clients.component.html',
  styleUrls: ['./clients.component.css']
})
export class ClientsComponent implements OnInit {
  dtOptions: DataTables.Settings = {};
  public clients: Client[];

  constructor(private http: HttpClient) { }


  ngOnInit(): void {
    this.dtOptions = {
      pagingType: 'full_numbers',
      pageLength: 10,
      processing: true
    };
    this.http.get<Client[]>('https://localhost:44363/api/clients', {
      headers: new HttpHeaders({
        "Content-Type": "application/json"
      })
    }).subscribe(result => {
      this.clients = result;
    }, error => {
      console.log(error)
    });
  }

  onSubmit(closet: any, drawer: any) {
    //get the value by its property
    console.log("Closet: " + closet);
    console.log("Drawer: " + drawer);
  }
}

這是相關的 csharp 文件

在我的啟動中,我對其進行了配置,以便使用 angular 並使用 jwt 鍵。

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("EnableCORS", builder => 
            {
                builder.AllowAnyOrigin()
                .AllowAnyHeader()
                .AllowAnyMethod();
            });
        });

        services.AddAuthentication(opt =>
        {
            opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(opttions =>
        {

            opttions.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,

                ValidIssuer = "https://localhost:44363",
                ValidAudience = "https://localhost:44363",
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@45"))
            };
        });

        services.AddControllersWithViews();
       
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/dist";
        });
        
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddTransient<DatabaseMigrator>();
        services.AddDbContext<erp_colombiaDbContext>(options => options.UseMySql(
                 Configuration.GetConnectionString("DefaultConnection"),
                 optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));

        services.AddDbContext<erp_colombiaDbContext>(options => options.UseMySql(
                Configuration.GetConnectionString("DefaultConnection"),
                optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();

        app.UseCors("EnableCORS");

        app.UseStaticFiles();
        if (!env.IsDevelopment())
        {
            app.UseSpaStaticFiles();
        }

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseAngularCliServer(npmScript: "start");
            }
        });
    }

這是我想從中獲取客戶列表的控制器

// GET api/clients
[HttpGet]
[Authorize]
public IEnumerable<ClientViewModel> Get()
{
    ClientViewModel clientViewModel;
    List<ClientViewModel> listClientViewModels = new List<ClientViewModel>();

    var clients = Task.Run(async () => await _clientService.GetAllClients()).Result;

    foreach (var client in clients) 
    {
        clientViewModel = new ClientViewModel();
        clientViewModel.ClientId = client.ClientId;
        clientViewModel.Active = client.Active;
        clientViewModel.Address = client.Address;
        clientViewModel.City = client.City;
        clientViewModel.ClienteName = client.ClienteName;
        clientViewModel.ComercialEmployeeId = client.ComercialEmployeeId;
        clientViewModel.Confirmed = client.Confirmed;
        clientViewModel.CountryId = client.CountryId;
        clientViewModel.CreationDate = client.CreationDate;
        clientViewModel.DANE = client.DANE;
        clientViewModel.Department = client.Department;
        clientViewModel.ElectronicBillingEmail = client.ElectronicBillingEmail;
        clientViewModel.Eliminated = client.Eliminated;
        clientViewModel.NIT = client.NIT;
        clientViewModel.PostalCode = client.PostalCode;
        clientViewModel.Phone = client.Phone;

        listClientViewModels.Add(clientViewModel);
    }

    return listClientViewModels;
}

如果我沒記錯的話,您收到的是 401,因為您的請求未Authorized

因此,您可以將Authorization標頭添加到您的請求中,例如:

headers: new HttpHeaders({
    "Authorization": `Bearer ${localStorage.getItem("jwt")}`,
    "Content-Type": "application/json"
})

但我強烈建議使用Interceptor為每個請求應用Authorization token (一次定義,無需復制代碼\\標頭等)

@Injectable()
export class ApplyJWTTokenInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        const idToken = localStorage.getItem("jwt");

        if (idToken) {
            const authClonedRequest = req.clone({
                headers: req.headers
                    .set('Authorization', `Bearer ${idToken}`)
            });
            return next.handle(authClonedRequest);
        }
        else {
            return next.handle(req);
        }
    }
}

建議檢查:

  1. HTTP攔截器
  2. 攔截請求和響應
  3. 工作演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM