2017-08-03 76 views
0

我們有選擇理想的方法來處理在分佈式應用程序的認證和權限的問題:如何管理Angular 4應用程序和應用程序之間的身份驗證(Spring Boot/security)?

  • 後端由JAVA EE(春季啓動)開發/ Tomcat服務器的

  • 前端是通過角4 /服務器的NodeJS開發

我們使用Spring安全框架,到目前爲止,我們已經使用了3個可用選項(HTTP Basic中,智威湯遜和OAuth2用戶),同時努力適應一個基因代碼由Jhipster項目評估,但不幸的是,仍然停留在想要認證的用戶信息的恢復中。

換句話說,用戶輸入他的用戶名和密碼,服務器通過URL(http://localhost:8080/api/authenticate

接收該信息通過在java和客戶端調試,我看到服務器已經恢復(用戶名/密碼),並向瀏覽器發送以下形式的令牌(對於Oauth2):

{「access_token」:「1b0ac85d-b4ed-463f-af3a-acbce7d28353」,「token_type」:「bearer」,「 refresh_token「:」ed2f9041-7726-4939-93ba-4816503e3859「,」expires_in「:1799,」scope「:」讀寫「}

但之後,我必須檢索已經該用戶的信息,將它們存儲在localStorage的(角4),並重新使用它們,這就是爲什麼叫第一web服務後上面引述的,我所說的第二個Web服務: (http://localhost:8080/auth/account

這時時,鏈接函數來檢索數據庫信息,發送具有用戶名/密碼=空查詢,這給我們一個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)); 
} 

在角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); 
        } 
       } 

principal.service:

   @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; 
       } 
      } 

任何想法如何,我可以完成這件事?

+0

我恐怕你的問題太長,不太清楚。也許你可以編輯它,並添加一個高層次的概述你想要達到的目標(誰需要OAuth2令牌,你使用的OAuth2流量)以及你嘗試過的。要描述身份驗證過程,您可以使用一系列步驟,其中每個步驟都說明由誰執行的操作以及結果是什麼。 –

+0

我的目標是管理角度4應用程序和彈簧引導應用程序之間的安全部分,如果您有教程或鏈接,我不知道這樣做的理想選擇(Ouath 2,HTTP Basic或JWT)在哪裏我可以找到更多的細節,我會感激你 –

回答

0

我的建議(一些可能的情況):

如果你想有一個外部身份驗證,使用ID連接(的OAuth2擴展名),它的ID令牌。使用OAuth2隱式流程,因此身份驗證重定向由您的Angular應用程序處理,而不是其服務器。然後,您可以使用ID令牌作爲後端身份驗證(將令牌作爲請求標頭Authorization: Bearer tokenvalue發送),或者您的後端可以使用ID令牌發佈自己的JWT,其中包含您需要的任何其他信息(角色,權限等) 。您不會使用OAuth2進行授權。您的後端將使用自己的方式來檢查訪問權限。

另一種方法是實現您自己的身份驗證方法(例如只是一個帶有憑據的簡單表單)而不是OpenID Connect。但ID連接給你一些優點:

  • 可能更安全比你自己實現
  • 不要將你的用戶的祕密在你的應用
  • 您可以使用多個身份驗證提供
相關問題