Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 28.03.2019, 12:28
Аспирант
Отправить личное сообщение для sniffysko Посмотреть профиль Найти все сообщения от sniffysko
 
Регистрация: 20.10.2009
Сообщений: 79

SSO авторизация с кроссдоменными запросами Windows server + Node.js + Angular
Добрый день! Вопрос относится как к Angular, так и к Node.js, так что пока не знаю куда писать. Есть проблема с молчаливой авторизацией. Есть Web сервера, развернутые под IIS в домене. Фронт написан на Angular, который обращается кроссдоменными запросами к бэкэнду, написанному на Node.js по REST API. Необходимо использовать SSO при входе на сервер.
На серверах бэка и фронта включена Kerberos аутентификация (Windows authentification enable, providers: Negotiate, NTLM; Configuration editor \ useAppPoolCredentials: true). УЗ, под которой работает APP pool имеет прописанный servicePrincipalName, в который включены url как фронта так и бэкэнда.
При входе на фронт происходит нормальная молчаливая авторизация. Т.е. в заголовках
Уходит: Authorization: Negotiate YIIIhgYGKw
И приходит: WWW-Authenticate: Negotiate oYGyMIG....

А вот когда начинаю отправлять запрос на бэкэнд из приложения, начинаются проблемы:
Уходит запрос на https://mysite.com/
1. получаем 401 Unauthorised
2. получаем 401 Unauthorised и Authorization: Negotiate YIIIkwYGKwYBBQUCoIIIhzCCCIOgMDAuBgkqh... (ключ отличается от того, который пришел в ответ на обращение с фронта. Наверное дело в этом?)
3. Выскакивет окно авторизации и пароль моей УЗ уже не подходит. Запрос не отрабатывает. Ошибка, как понимаю, принципиальная, прошу подсказать что делаю не так. Спасибо.


Для поддержки авторизации Angular использует Interseptor
Для поддержки авторизации Node.js использует модуль node-sspi. Используется express.

Содержимое проекта Node.js

var express = require('express');
var app = express();
var server = require('http').createServer(app);

const ALLOWED_ORIGINS = [
	'https://mysite.com',
	'http://mysite.com'
];

let out = {};

app.use((req, res, next) => {
	if(ALLOWED_ORIGINS.indexOf(req.headers.origin) > -1) {
		res.set('Access-Control-Allow-Credentials', 'true');
		res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
	}else{
		res.setHeader('Access-Control-Allow-Origin', '*');
	}
	res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE, CONNECT');
	res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-type, Token, Accept, Cache-Control, Authorization');
	next();
});


app.use((req, res, next) => {
	var nodeSSPI = require('node-sspi');
	var nodeSSPIObj = new nodeSSPI({
		retrieveGroups: true
	});
	nodeSSPIObj.authenticate(req, res, (err) => {
		res.finished || next();
	});
});



app.use((req, res, next) => {
	out = {
		user: req.connection.user,
		sid: req.connection.userSid,
		groups: []
	}

	if (req.connection.userGroups) {
		for (let i in req.connection.userGroups) {
			out.groups.push(req.connection.userGroups[i]);
		}
	}
});

app.get('/:id', (req, res, next) => {
	const server = `Server run at ${process.env.PORT || 3000}`;
	let str = JSON.stringify({server: server, auth: out});
	console.log(str);	
	res.send( str );
});


// Start server
var port = process.env.PORT || 3000;
server.listen(port, function () {
	console.log('Express server listening on port %d in %s mode', port, app.get('env'))
});


Содержимое проекта Angular

app.module.ts

@NgModule({
	declarations: [
		AppComponent
	],
	imports: [
		BrowserModule,
		HttpClientModule
	],
	providers: [
		{
			provide: HTTP_INTERCEPTORS,
			useClass: WinAuthInterceptor,
			multi: true
		},
		AuthenticationService,
		DataService
	],
	bootstrap: [AppComponent]
})
export class AppModule { }


winauth-interceptor.ts

@Injectable()
export class WinAuthInterceptor implements HttpInterceptor{
  constructor(){}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({
      withCredentials: true
    });
    return next.handle(req);
  }
}


Authentication.service.ts генерим рандомные запросы

@Injectable()
export class AuthenticationService {

	constructor(
		private http: HttpClient
	) { }

	getUser(): Observable<string> {
		console.log('Calling getUser');
		let serviceUrl: string = `https://apisite.com/${ Math.floor( Math.random() * 100 ) }`;
		return this.http.get<string>(serviceUrl, {}).pipe(
			map( (data: string) => {
				console.log("Received: " + JSON.stringify(data));
				return data;
			})
		);
	}
}


app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private authRslt: string = '';

  constructor(
    private http: HttpClient,
    private authSvc: AuthenticationService
  ){}

  testAuthentication(): void {
    this.authSvc.getUser()
      .subscribe(
        r => {this.authRslt = JSON.stringify(r);},
        e => {
          console.log(e); 
          this.authRslt = 'error';
        }
      );
  }
}


app.component.html

<div style="text-align:center"><h1>Auth</h1></div>
<div style="text-align:center">
    <div class="col-lg-12">
        <button type="button" (click)="testAuthentication()">Authentication</button>
        <pre style="border:solid 1px;min-height:21px;">{{authRslt}}</pre>
    </div>
</div>
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск