Пролетна страница за вход за сигурност с Angular

1. Общ преглед

В този урок ще създадем страница за вход, използвайки Spring Security с:

  • AngularJS
  • Ъглови 2, 4, 5 и 6

Примерното приложение, което ще обсъдим тук, се състои от клиентско приложение, което комуникира с услугата REST, защитено с основно HTTP удостоверяване.

2. Пролетна конфигурация на защитата

Първо, нека да настроим REST API с Spring Security и Basic Auth:

Ето как е конфигуриран:

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

Сега нека създадем крайните точки. Нашата услуга REST ще има две - едната за влизане и другата за извличане на потребителските данни:

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

По същия начин можете да разгледате и другия ни урок за Spring Security OAuth2, ако се интересувате от внедряването на OAuth2 сървър за оторизация.

3. Настройка на ъгловия клиент

След като създадохме услугата REST, нека настроим страницата за вход с различни версии на Angular клиента.

Примерите, които ще видим тук, използват npm за управление на зависимости и nodejs за стартиране на приложението.

Angular използва архитектура от една страница, където всички дъщерни компоненти (в нашия случай това са компоненти за вход и начало) се инжектират в общ родителски DOM.

За разлика от AngularJS, който използва JavaScript, Angular версия 2 нататък използва TypeScript като основен език. Следователно приложението също изисква определени поддържащи файлове, които са необходими за правилната му работа.

Поради допълнителните подобрения на Angular, необходимите файлове се различават от версия на версия.

Нека се запознаем с всеки от тях:

  • systemjs.config.js - системни конфигурации (версия 2)
  • package.json - зависимости от модулен възел (версия 2 нататък)
  • tsconfig.json - конфигурации на Typescript на корен ниво (версия 2 нататък)
  • tsconfig.app.json - конфигурации на Typescript на ниво приложение (версия 4 нататък)
  • .angular- cli .json - ъглови CLI конфигурации (версия 4 и 5)
  • angular.json - ъглови CLI конфигурации (версия 6 нататък)

4. Страница за вход

4.1. Използване на AngularJS

Нека създадем файла index.html и добавим съответните зависимости към него:

Тъй като това е приложение на една страница, всички дъщерни компоненти ще бъдат добавени към елемента div с атрибут ng-view въз основа на логиката на маршрутизиране.

Сега нека създадем app.js, който дефинира URL адреса за съпоставяне на компоненти:

(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'); } }); } })();

Компонентът за влизане се състои от два файла, login.controller.js и login.view.html.

Нека разгледаме първия:

Login

Username Username is required Password Password is required Login

и второто:

(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.") } }); }; } })();

Контролерът ще извика услугата REST, като предаде потребителското име и паролата. След успешното удостоверяване той ще кодира потребителското име и паролата и ще съхрани кодирания маркер в хранилището на сесията за бъдеща употреба.

Подобно на компонента за вход, компонентът home също се състои от два файла, home.view.html :

You're logged in!!

Logout

и 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'; } } })();

Домашният контролер ще изиска потребителските данни, като предаде заглавката за оторизация . Нашата услуга REST ще върне потребителските данни само ако токенът е валиден.

Сега нека инсталираме http-сървър за стартиране на приложението Angular:

npm install http-server --save

След като това бъде инсталирано, можем да отворим основната папка на проекта в командния ред и да изпълним командата:

http-server -o

4.2. Използване на ъглова версия 2, 4, 5

В index.html във версия 2, се различава незначително от версията на AngularJS:

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

На main.ts е основната входна точка на заявлението. Той зарежда модула на приложението и в резултат браузърът зарежда страницата за вход:

platformBrowserDynamic().bootstrapModule(AppModule);

На app.routing.ts носи отговорност за маршрутизиране на приложение:

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.

Вместо HttpModule , версия 6 импортира HttpClientModule от @ angular / common / http.

Частта за повикване на услугата също ще бъде малко по-различна от по-старите версии:

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. Заключение

Научихме как да внедрим страница за влизане в Spring Security с Angular. От версия 4 нататък можем да използваме проекта Angular CLI за лесно разработване и тестване.

Както винаги всички примери, които обсъдихме тук, могат да бъдат намерени в проекта GitHub.