Spring Security-Anmeldeseite mit Angular

1. Übersicht

In diesem Tutorial erstellen wir eine Anmeldeseite mit Spring Security mit:

  • AngularJS
  • Winkel 2, 4, 5 und 6

Die Beispielanwendung, die wir hier diskutieren werden, besteht aus einer Clientanwendung, die mit dem REST-Service kommuniziert und durch eine grundlegende HTTP-Authentifizierung gesichert ist.

2. Spring Security-Konfiguration

Lassen Sie uns zunächst die REST-API mit Spring Security und Basic Auth einrichten:

So ist es konfiguriert:

@Configuration @EnableWebSecurity public class BasicAuthConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user") .password("password") .roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest() .authenticated() .and() .httpBasic(); } }

Jetzt erstellen wir die Endpunkte. Unser REST-Service verfügt über zwei Funktionen - eine zum Anmelden und eine zum Abrufen der Benutzerdaten:

@RestController @CrossOrigin public class UserController { @RequestMapping("/login") public boolean login(@RequestBody User user) { return user.getUserName().equals("user") && user.getPassword().equals("password"); } @RequestMapping("/user") public Principal user(HttpServletRequest request) { String authToken = request.getHeader("Authorization") .substring("Basic".length()).trim(); return () -> new String(Base64.getDecoder() .decode(authToken)).split(":")[0]; } }

Ebenso können Sie auch unser anderes Tutorial zu Spring Security OAuth2 lesen, wenn Sie einen OAuth2-Server zur Autorisierung implementieren möchten.

3. Einrichten des Angular Clients

Nachdem wir den REST-Service erstellt haben, richten wir die Anmeldeseite mit verschiedenen Versionen des Angular-Clients ein.

Die Beispiele, die wir hier sehen werden, verwenden npm für das Abhängigkeitsmanagement und nodejs für die Ausführung der Anwendung.

Angular verwendet eine einzelne Seitenarchitektur, bei der alle untergeordneten Komponenten (in unserem Fall Login- und Home-Komponenten) in ein gemeinsames übergeordnetes DOM eingefügt werden.

Im Gegensatz zu AngularJS, das JavaScript verwendet, verwendet Angular ab Version 2 TypeScript als Hauptsprache. Daher benötigt die Anwendung auch bestimmte unterstützende Dateien, die für die ordnungsgemäße Funktion erforderlich sind.

Aufgrund der inkrementellen Verbesserungen von Angular unterscheiden sich die benötigten Dateien von Version zu Version.

Machen wir uns mit jedem dieser Punkte vertraut:

  • systemjs.config.js - Systemkonfigurationen (Version 2)
  • package.json - Knotenmodulabhängigkeiten (ab Version 2)
  • tsconfig.json - Typescript-Konfigurationen auf Stammebene (ab Version 2)
  • tsconfig.app.json - Typescript-Konfigurationen auf Anwendungsebene (ab Version 4)
  • .angular- cli .json - Angular CLI-Konfigurationen (Version 4 und 5)
  • angle.json - Angular CLI-Konfigurationen (ab Version 6)

4. Anmeldeseite

4.1. Verwenden von AngularJS

Lassen Sie uns die Datei index.html erstellen und die relevanten Abhängigkeiten hinzufügen:

Da es sich um eine Einzelseitenanwendung handelt, werden alle untergeordneten Komponenten dem div-Element mit dem Attribut ng-view basierend auf der Routing-Logik hinzugefügt .

Erstellen wir nun die Datei app.js, die die URL zur Komponentenzuordnung definiert:

(function () { 'use strict'; angular .module('app', ['ngRoute']) .config(config) .run(run); config.$inject = ['$routeProvider', '$locationProvider']; function config($routeProvider, $locationProvider) { $routeProvider.when('/', { controller: 'HomeController', templateUrl: 'home/home.view.html', controllerAs: 'vm' }).when('/login', { controller: 'LoginController', templateUrl: 'login/login.view.html', controllerAs: 'vm' }).otherwise({ redirectTo: '/login' }); } run.$inject = ['$rootScope', '$location', '$http', '$window']; function run($rootScope, $location, $http, $window) { var userData = $window.sessionStorage.getItem('userData'); if (userData) { $http.defaults.headers.common['Authorization'] = 'Basic ' + JSON.parse(userData).authData; } $rootScope .$on('$locationChangeStart', function (event, next, current) { var restrictedPage = $.inArray($location.path(), ['/login']) === -1; var loggedIn = $window.sessionStorage.getItem('userData'); if (restrictedPage && !loggedIn) { $location.path('/login'); } }); } })();

Die Anmeldekomponente besteht aus zwei Dateien, der Datei login.controller.js und der Datei login.view.html.

Schauen wir uns den ersten an:

Login

Username Username is required Password Password is required Login

und der zweite:

(function () { 'use strict'; angular .module('app') .controller('LoginController', LoginController); LoginController.$inject = ['$location', '$window', '$http']; function LoginController($location, $window, $http) { var vm = this; vm.login = login; (function initController() { $window.localStorage.setItem('token', ''); })(); function login() { $http({ url: '//localhost:8082/login', method: "POST", data: { 'userName': vm.username, 'password': vm.password } }).then(function (response) { if (response.data) { var token = $window.btoa(vm.username + ':' + vm.password); var userData = { userName: vm.username, authData: token } $window.sessionStorage.setItem( 'userData', JSON.stringify(userData) ); $http.defaults.headers.common['Authorization'] = 'Basic ' + token; $location.path('/'); } else { alert("Authentication failed.") } }); }; } })();

Der Controller ruft den REST-Service auf, indem er den Benutzernamen und das Kennwort übergibt. Nach der erfolgreichen Authentifizierung werden der Benutzername und das Kennwort verschlüsselt und das verschlüsselte Token zur späteren Verwendung im Sitzungsspeicher gespeichert.

Ähnlich wie die Anmeldekomponente besteht auch die Home-Komponente aus zwei Dateien, der home.view.html :

You're logged in!!

Logout

und die home.controller.js:

(function () { 'use strict'; angular .module('app') .controller('HomeController', HomeController); HomeController.$inject = ['$window', '$http', '$scope']; function HomeController($window, $http, $scope) { var vm = this; vm.user = null; initController(); function initController() { $http({ url: '//localhost:8082/user', method: "GET" }).then(function (response) { vm.user = response.data.name; }, function (error) { console.log(error); }); }; $scope.logout = function () { $window.sessionStorage.setItem('userData', ''); $http.defaults.headers.common['Authorization'] = 'Basic'; } } })();

Der Heimcontroller fordert die Benutzerdaten an, indem er den Autorisierungsheader übergibt . Unser REST-Service gibt die Benutzerdaten nur zurück, wenn das Token gültig ist.

Installieren wir nun den http-Server zum Ausführen der Angular-Anwendung:

npm install http-server --save

Sobald dies installiert ist, können wir den Projektstammordner in der Eingabeaufforderung öffnen und den folgenden Befehl ausführen:

http-server -o

4.2. Verwenden von Angular Version 2, 4, 5

Die index.html in Version 2 unterscheidet sich geringfügig von der AngularJS-Version:

         System.import('app').catch(function (err) { console.error(err); });    Loading...  

Die main.ts ist der Haupteinstiegspunkt der Anwendung. Das Anwendungsmodul wird gebootet und der Browser lädt die Anmeldeseite:

platformBrowserDynamic().bootstrapModule(AppModule);

Die app.routing.ts ist für das Anwendungsrouting verantwortlich:

const appRoutes: Routes = [ { path: '', component: HomeComponent }, { path: 'login', component: LoginComponent }, { path: '**', redirectTo: '' } ]; export const routing = RouterModule.forRoot(appRoutes);

The app.module.ts declares the components and imports the relevant modules:

@NgModule({ imports: [ BrowserModule, FormsModule, HttpModule, routing ], declarations: [ AppComponent, HomeComponent, LoginComponent ], bootstrap: [AppComponent] }) export class AppModule { }

Since we're creating a single page application, let's create a root component which adds all the child components to it:

@Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { }

The app.component.html will have only a tag. The Angular uses this tag for its location routing mechanism.

Now let's create the login component and its corresponding template in login.component.ts:

@Component({ selector: 'login', templateUrl: './app/login/login.component.html' }) export class LoginComponent implements OnInit { model: any = {}; constructor( private route: ActivatedRoute, private router: Router, private http: Http ) { } ngOnInit() { sessionStorage.setItem('token', ''); } login() { let url = '//localhost:8082/login'; let result = this.http.post(url, { userName: this.model.username, password: this.model.password }).map(res => res.json()).subscribe(isValid => { if (isValid) { sessionStorage.setItem( 'token', btoa(this.model.username + ':' + this.model.password) ); this.router.navigate(['']); } else { alert("Authentication failed."); } }); } }

Finally, let's have a look at the login.component.html:

 Username Username is required Password Password is required Login 

4.3. Using Angular 6

Angular team has made some enhancements in version 6. Due to these changes, our example will also be a little different compared to other versions. The only change we've in our example with respect to version 6 is in the service calling part.

Anstelle von HttpModule importiert die Version 6 HttpClientModule aus @ angle / common / http.

Der Dienstaufrufteil unterscheidet sich auch ein wenig von älteren Versionen:

this.http.post
    
     (url, { userName: this.model.username, password: this.model.password }).subscribe(isValid => { if (isValid) { sessionStorage.setItem( 'token', btoa(this.model.username + ':' + this.model.password) ); this.router.navigate(['']); } else { alert("Authentication failed.") } });
    

5. Schlussfolgerung

Wir haben gelernt, wie Sie eine Spring Security-Anmeldeseite mit Angular implementieren. Ab Version 4 können wir das Angular CLI-Projekt für eine einfache Entwicklung und Tests verwenden.

Wie immer finden Sie alle hier diskutierten Beispiele über das GitHub-Projekt.