[英]How to Manage Authentication between an Angular 4 Application and an Application (Spring Boot / security)?
我們在選擇理想的方法來處理分布式應用程序中的身份驗證和權限時遇到問題:
后端是由JAVA EE(春季啟動)/ Tomcat Server開發的。
前端由Angular 4 / NodeJS服務器開發
我們使用的是Spring Security Framework,到目前為止,在嘗試改編Jhipster項目生成的代碼時,我們已經使用了3個可用選項(HTTP Basic,JWT和Oauth2),但是不幸的是,其中一個仍然停留在用戶信息的恢復中誰想要認證。
換句話說,用戶輸入其登錄名和密碼,服務器通過URL( http:// localhost:8080 / api / authenticate )接收此信息。
通過在Java和客戶端進行調試,我發現服務器已恢復(用戶名/ PWD),並且它以以下形式(對於Oauth2)將令牌發送到瀏覽器:
{ “的access_token”: “1b0ac85d-b4ed-463f-af3a-acbce7d28353”, “token_type”: “承載”, “refresh_token”: “ed2f9041-7726-4939-93ba-4816503e3859”, “expires_in”:1799, “范圍” :“讀寫”}
但是之后,我必須檢索該用戶的信息以將其存儲在LocalStorage(Angular 4)中並重新使用它們,這就是為什么在調用上面引用的第一個Web服務之后,我調用第二個Web服務:( http://本地主機:8080 / auth / account )
這時,鏈接函數檢索數據庫信息,發送用戶名/ pwd = null的查詢,這給了我們500條消息
在Java應用程序中:
/**
* GET /authenticate : check if the user is authenticated, and return its
* login.
*
* @param request
* the HTTP request
* @return the login if the user is authenticated
*/
@RequestMapping(value = ApiConstants.API_ANONYME + "/authenticate", method = RequestMethod.GET)
public String isAuthenticated(HttpServletRequest request) {
log.debug("REST request to check if the current user is authenticated");
String remoteUser = request.getRemoteUser();
return remoteUser;
}
/**
* GET /account : get the current user.
*
* @return the ResponseEntity with status 200 (OK) and the current user in
* body, or status 500 (Internal Server Error) if the user couldn't
* be returned
*/
// @GetMapping("/account")
@RequestMapping(value = ApiConstants.API_AUTH + "/account", method = RequestMethod.GET)
public ResponseEntity<Utilisateur> getAccount() {
Utilisateur userWithAuthorities = userService.getUserWithAuthorities();
return Optional.ofNullable(userWithAuthorities)
.map(user -> new ResponseEntity<>(new Utilisateur(), HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
在Angular 4應用程序中:
export class ConnexionBodyComponent implements OnInit {
// model = new Login("", "");
authenticationError: boolean;
password: string;
rememberMe: boolean;
username: string;
credentials: any;
constructor(private loginService: LoginService, private router: Router,
private eventManager: EventManager, private stateStorageService:
StateStorageService) {
}
ngOnInit() {
}
login() {
this.loginService.login({
username: this.username,
password: this.password,
rememberMe: this.rememberMe
}).then(() => {
this.authenticationError = false;
/*if (this.router.url === '/register' ||
(/activate/.test(this.router.url)) ||
this.router.url === '/finishReset' || this.router.url ===
'/requestReset') {
this.router.navigate(['']);
}*/
console.log(this.authenticationError);
this.eventManager.broadcast({
name: 'authenticationSuccess',
content: 'Sending Authentication Success'
});
// // previousState was set in the authExpiredInterceptor before being
redirected to login modal.
// // since login is succesful, go to stored previousState and clear
previousState
const redirect = this.stateStorageService.getUrl();
if (redirect) {
this.router.navigate([redirect]);
}
}).catch(() => {
this.authenticationError = true;
});
}
}
AccountService:
@Injectable()
export class AccountService {
constructor(private http: Http) { }
get(): Observable<any> {
return
this.http.get(LocalhostSettings.API_ENDPOINT+'/auth/account').map((res:
Response) => res.json());
}
save(account: any): Observable<Response> {
return
this.http.post(LocalhostSettings.API_ENDPOINT+'/auth/account',
account);
}
}
auth.service.ts:
@Injectable()
export class AuthService {
constructor(
private principal: Principal,
private stateStorageService: StateStorageService,
private router: Router
) {}
authorize(force) {
const authReturn = this.principal.identity(force).then(authThen.bind(this));
return authReturn;
function authThen() {
const isAuthenticated = this.principal.isAuthenticated();
const toStateInfo = this.stateStorageService.getDestinationState().destination;
// an authenticated user can't access to login and register pages
if (isAuthenticated && (toStateInfo.name === 'register')) {
this.router.navigate(['']);
return false;
}
// recover and clear previousState after external login redirect (e.g. oauth2)
const fromStateInfo = this.stateStorageService.getDestinationState().from;
const previousState = this.stateStorageService.getPreviousState();
if (isAuthenticated && !fromStateInfo.name && previousState) {
this.stateStorageService.resetPreviousState();
this.router.navigate([previousState.name], { queryParams: previousState.params });
return false;
}
if (toStateInfo.data.authorities && toStateInfo.data.authorities.length > 0) {
return this.principal.hasAnyAuthority(toStateInfo.data.authorities).then((hasAnyAuthority) => {
if (!hasAnyAuthority) {
if (isAuthenticated) {
// user is signed in but not authorized for desired state
this.router.navigate(['accessdenied']);
} else {
// user is not authenticated. Show the state they wanted before you
// send them to the login service, so you can return them when you're done
const toStateParamsInfo = this.stateStorageService.getDestinationState().params;
this.stateStorageService.storePreviousState(toStateInfo.name, toStateParamsInfo);
// now, send them to the signin state so they can log in
this.router.navigate(['accessdenied']).then(() => {
console.log('accessdenied');
});
}
}
return hasAnyAuthority;
});
}
return true;
}
}
}
auth-oauth2.service.ts:
@Injectable()
export class AuthServerProvider {
constructor(
private http: Http,
private base64: Base64,
private $localStorage: LocalStorageService
) {}
getToken() {
return this.$localStorage.retrieve('authenticationToken');
}
login(credentials): Observable<any> {
const data = 'username=' + encodeURIComponent(credentials.username) + '&password=' +
encodeURIComponent(credentials.password) + '&grant_type=password&scope=read%20write&' +
'client_secret=my-secret-token-to-change-in-production&client_id=directinfoapp';
const headers = new Headers ({
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
'Authorization': 'Basic ' + this.base64.encode('directinfoapp' + ':' + 'my-secret-token-to-change-in-production')
});
/*
{"access_token":"ff102054-2072-4c29-91a8-c8f43246a3b7","token_type":"bearer","refresh_token":"94b68064-98a6-49e5-9dbc-0cac34e434f3","expires_in":1799,"scope":"read write"}
*/
console.log("headers : " + headers);
return this.http.post(LocalhostSettings.API_ENDPOINT+'/oauth/token', data, {
headers
}).map(authSuccess.bind(this));
function authSuccess(resp) {
const response = resp.json();
console.log("authSuccess " + resp.json())
const expiredAt = new Date();
expiredAt.setSeconds(expiredAt.getSeconds() + response.expires_in);
response.expires_at = expiredAt.getTime();
this.$localStorage.store('authenticationToken', response);
return response;
}
}
logout(): Observable<any> {
return new Observable(observer => {
this.http.post('api/logout', {});
this.$localStorage.clear('authenticationToken');
observer.complete();
});
}
}
login.service.ts
@Injectable()
export class LoginService {
constructor(
private principal: Principal,
private authServerProvider: AuthServerProvider
) {}
login(credentials, callback?) {
const cb = callback || function() {};
return new Promise((resolve, reject) => {
this.authServerProvider.login(credentials).subscribe((data) => {
this.principal.identity(true).then((account) => {
// After the login the language will be changed to
// the language selected by the user during his registration
if (account !== null) {
account.langKey;
}
resolve(data);
});
return cb();
}, (err) => {
this.logout();
reject(err);
return cb(err);
});
});
}
logout() {
this.authServerProvider.logout().subscribe();
this.principal.authenticate(null);
}
}
主體服務:
@Injectable()
export class Principal {
private userIdentity: any;
private authenticated = false;
private authenticationState = new Subject<any>();
constructor(
private account: AccountService
) {}
authenticate(identity) {
this.userIdentity = identity;
this.authenticated = identity !== null;
this.authenticationState.next(this.userIdentity);
}
hasAnyAuthority(authorities: string[]): Promise<boolean> {
if (!this.authenticated || !this.userIdentity || !this.userIdentity.authorities) {
return Promise.resolve(false);
}
for (let i = 0; i < authorities.length; i++) {
if (this.userIdentity.authorities.indexOf(authorities[i]) !== -1) {
return Promise.resolve(true);
}
}
return Promise.resolve(false);
}
hasAuthority(authority: string): Promise<boolean> {
if (!this.authenticated) {
return Promise.resolve(false);
}
return this.identity().then((id) => {
return Promise.resolve(id.authorities && id.authorities.indexOf(authority) !== -1);
}, () => {
return Promise.resolve(false);
});
}
identity(force?: boolean): Promise<any> {
if (force === true) {
this.userIdentity = undefined;
}
// check and see if we have retrieved the userIdentity data from the server.
// if we have, reuse it by immediately resolving
if (this.userIdentity) {
return Promise.resolve(this.userIdentity);
}
// retrieve the userIdentity data from the server, update the identity object, and then resolve.
return this.account.get().toPromise().then((account) => {
if (account) {
this.userIdentity = account;
this.authenticated = true;
} else {
this.userIdentity = null;
this.authenticated = false;
}
this.authenticationState.next(this.userIdentity);
return this.userIdentity;
}).catch((err) => {
this.userIdentity = null;
this.authenticated = false;
this.authenticationState.next(this.userIdentity);
return null;
});
}
isAuthenticated(): boolean {
return this.authenticated;
}
isIdentityResolved(): boolean {
return this.userIdentity !== undefined;
}
getAuthenticationState(): Observable<any> {
return this.authenticationState.asObservable();
}
getImageUrl(): String {
return this.isIdentityResolved() ? this.userIdentity.imageUrl : null;
}
}
有什么想法我可以完成嗎?
我的建議(一些可能的情況):
如果要進行外部身份驗證,請使用OpenID Connect(OAuth2擴展名)及其ID令牌。 使用OAuth2隱式流程,因此身份驗證重定向由Angular應用程序而不是其服務器處理。 然后,您可以使用ID令牌作為后端的身份驗證(將令牌作為請求標Authorization: Bearer tokenvalue
),或者您的后端可以使用ID令牌發布自己的JWT,其中包含您需要的任何其他信息(角色,權限等)。 您不會使用OAuth2進行授權。 您的后端將使用其自己的方式來檢查訪問權限。
另一種方法是實現您自己的身份驗證方法(例如,僅帶有憑據的簡單表單),而不是OpenID Connect。 但是OpenID Connect具有一些優勢:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.