Страница входа Spring Security с угловым
1. обзор
В этом уроке мы создадимlogin page using Spring Security with:
-
AngularJS
-
Угловые 2, 4, 5 и 6
Пример приложения, которое мы собираемся здесь обсудить, состоит из клиентского приложения, которое взаимодействует со службой REST и защищено базовой HTTP-аутентификацией.
2. Конфигурация Spring Security
Прежде всего, давайте настроим 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. Настройка клиента Angular
Теперь, когда мы создали службу REST, давайте настроим страницу входа с различными версиями клиента Angular.
В примерах, которые мы собираемся здесь увидеть, используетсяnpm для управления зависимостями иnodejs для запуска приложения.
Angular использует одностраничную архитектуру, в которой все дочерние компоненты (в нашем случае это компоненты входа в систему и домашние компоненты) вставляются в общую родительскую DOM.
В отличие от AngularJS, который использует JavaScript, Angular версии 2 и далее использует TypeScript в качестве основного языка. Следовательно, приложению также требуются определенные вспомогательные файлы, которые необходимы для его корректной работы.
Из-за дополнительных улучшений Angular, необходимые файлы отличаются от версии к версии.
Давайте познакомимся с каждым из них:
-
systemjs.config.js - конфигурации системы (версия 2)
-
package.json - зависимости узловых модулей (начиная с версии 2)
-
tsconfig.json - конфигурации машинописного текста корневого уровня (начиная с версии 2)
-
tsconfig.app.json - конфигурации Typescript уровня приложения (начиная с версии 4)
-
.angular-cli_.json_ - Конфигурации Angular CLI (версии 4 и 5)
-
angular.json - Конфигурации Angular 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
и второй:
(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: 'http://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.view.html:
Hi {{vm.user}}!
You're logged in!!
и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: 'http://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';
}
}
})();
Домашний контроллер запросит пользовательские данные, передав заголовокAuthorization. Наша служба REST вернет данные пользователя только в том случае, если токен действителен.
Теперь давайте установимhttp-server для запуска приложения Angular:
npm install http-server --save
После того, как это установлено, мы можем открыть корневую папку проекта в командной строке и выполнить команду:
http-server -o
4.2. Использование Angular версии 2, 4, 5
index.html в версии 2 немного отличается от версии AngularJS:
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);
app.module.ts объявляет компоненты и импортирует соответствующие модули:
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
routing
],
declarations: [
AppComponent,
HomeComponent,
LoginComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
Поскольку мы создаем одностраничное приложение, давайте создадим корневой компонент, который добавляет к нему все дочерние компоненты:
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent { }
app.component.html будет иметь только тег<router-outlet>. Angular использует этот тег для своего механизма маршрутизации местоположения.
Теперь давайте создадим компонент входа в систему и соответствующий ему шаблон в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 = 'http://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.");
}
});
}
}
Наконец, давайте посмотрим наlogin.component.html:
4.3. Использование Angular 6
Команда Angular внесла некоторые улучшения в версию 6. Из-за этих изменений наш пример также будет немного отличаться от других версий. Единственное изменение в нашем примере по сравнению с версией 6 - это часть вызова службы.
Вместо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 project.