Проблема с получением информации о пользователях AD в angular из ядра .net - PullRequest
1 голос
/ 24 октября 2019

Мое приложение состоит из двух отдельных проектов: .NET core back-end и Angular front-end.

Я успешно добавил Windows ADаутентификации и я получаю информацию о пользователе внутри моего приложения Angular, но только после обновления страницы.

При первом открытии страницы мой пользователь имеет значение null

ERROR TypeError: Cannot read property 'name' of null

После обновления я могуполучить информацию о пользователе. Что на самом деле происходит и как мне исправить эту проблему?

public Task < AdUser > GetAdUser(Guid guid) {
  return Task.Run(() => {
    try {
      PrincipalContext context = new PrincipalContext(ContextType.Domain);
      UserPrincipal principal = new UserPrincipal(context);

      if (context != null) {
        principal = UserPrincipal.FindByIdentity(context, IdentityType.Guid,
          guid.ToString());
      }

      return AdUser.CastToAdUser(principal);
    } catch (Exception ex) {
      throw new Exception("Error retrieving AD User", ex);
    }
  });
}

identity.service.ts

getCurrentUser() {
    return this.http.get(this.apiUrl + '/identity/GetAdUser', { withCredentials: true });
}

app.component.rs

this.identityService.getCurrentUser()
.subscribe(res => {
localStorage.setItem('user', JSON.stringify(res));
});

top-navigation.component.ts

user = JSON.parse(localStorage.getItem('user'));

Наконец, я получаю пользовательские данные внутри моего топ-навигационного компонента, потому что мне нужно, чтобы имя и фамилия отображались внутри него.

РЕДАКТИРОВАТЬ: добавлен новый код

app-routing.module.ts

    const appRoutes: Routes = [
   {
    path: 'home',
    component: HomeComponent,
    data: { title: 'Home' },
    canActivate: [AuthGuardService]
  },
  {
    path: 'history',
    component: HistoryComponent,
    data: { title: 'History' }
  },
  {
    path: '404',
    component: NotFoundErrorComponent,
    data: { title: 'Error 404' }
  },
  {
    path: '',
    component: HomeComponent,
    pathMatch: 'full'
  },
  {
    path: '**',
    redirectTo: '/404',
    pathMatch: 'full',
    data: { title: 'Error 404' }
  }
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.ts

    @Component({
     selector: 'app-root',
     templateUrl: './app.component.html',
     styleUrls: ['./app.component.scss']
    })
    export class AppComponent implements OnInit {
     title = 'Home';

    constructor(private titleService: Title,
              private router: Router,
              private activatedRoute: ActivatedRoute { }

    ngOnInit() {
    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd),
      map(() => this.activatedRoute),
      map((route) => {
        while (route.firstChild) { route = route.firstChild; }
        return route;
      }),
      filter((route) => route.outlet === 'primary'),
      mergeMap((route) => route.data))
      .subscribe((event) => this.titleService.setTitle(event.title));
     }
     }

top-navigation.component.ts

    export class TopNavigationComponent implements OnInit {
      constructor(private router: Router) { }

      user;

      ngOnInit() {
       this.user = JSON.parse(localStorage.getItem('user'));
      }
     }

РЕДАКТИРОВАТЬ: окончательное решение

app-routing.module.ts

const appRoutes: Routes = [
{
path: '',
component: TopNavComponent,
resolve: { userData: IdentityResolverService },
children: [
  {
    path: 'home',
    component: HomeComponent,
    data: { title: 'Home' },
    canActivate: [AuthGuardService]
  },
  {
    path: 'history',
    component: HistoryComponent,
    data: { title: 'History' },
    canActivate: [AuthGuardService]
  },
  {
    path: '404',
    component: NotFoundErrorComponent,
    data: { title: 'Error 404' },
    canActivate: [AuthGuardService]
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full',
    canActivate: [AuthGuardService]
  },
  {
    path: '**',
    redirectTo: '/404',
    pathMatch: 'full',
    data: { title: 'Error 404' },
    canActivate: [AuthGuardService]
  }
]
};
]

1 Ответ

1 голос
/ 24 октября 2019

Хорошо, я попытаюсь объяснить, как все это работает. Ваша проблема в том, что getCurrentUser из вашего компонента выполняется после того, как localStorage пытается получить данные.

Если ваш AppComponent является корневым компонентом вашего приложения и top-navigation компонент объявлен в html компонента приложения, тогда вам нужно убедиться, что данные там есть до запуска AppComponent.

Итак, что происходит (возможная временная шкала какасинхронная природа javascript означает, что иногда некоторые шаги будут выполняться в другом порядке):

  1. Приложение направляется на ваш основной маршрут
  2. Конструктор AppComponent выполняет
  3. Конструктор TopNavigationComponent выполняет
  4. HTTP-вызов getCurrentUser () начинается
  5. TopNavigationComponent пытается получить значение из localStorage (лучше использовать SessionStorage по соображениям безопасности) и завершается с ошибкой
  6. HTTP-вызов getCurrentUser () завершается, и AppComponent устанавливает значение localStorageUE
  7. Вы обновляете. Когда вы дойдете до шага 5., локальное хранилище было установлено с шага 6.

Лучшим решением будет использование защиты. Это более справедливо для вашего случая, если вам нужно использовать ADUser в качестве функции безопасности. В случае сбоя защиты вы не перейдете к компоненту AppComponent.

Объявите Guard , а если данные есть, перейдите к компоненту приложения, и охранник убедится, чтоу вас есть данные ADUser в локальном хранилище (или хранилище сеанса).

resolver (другое возможное решение) всегда будет работать до загрузки AppComponent и не будет блокироваться, таким образом, убедившись, чточто у вас есть данные, если приложение ответило.

Подробнее о охранниках и распознавателях здесь

Посмотрите на следующего охранника:

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private adUserService: ADUserService, 
    private router: Router
  ) {}

  canActivate(next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> {
    // We will return a true if the user is logged in
    // or false otherwise
    return this.adUserService.login().pipe(
      map((x) => {
        // Since my service returned succesfully I will set the
        // locale storage with the appropriate data
        localStorage.setItem('user', JSON.stringify(x));
        // I will also return true to let the router module
        // know that it can proceed
        return true;
      }),
      catchError(() => {
        // If there was an error, I will return false, so the
        // router module will not allow the transition
        // if I want to, I can add the router module to transition
        // to a login page
        return of(false)
      })
    );
  }
}

Вы можете проверить полное решение здесь: Пример stackblitz

Чтобы увидеть хранилище локали в действии, попробуйте https://soangular-route-guards-locale-storage.stackblitz.io/user url.

...