Spring Вход для системы безопасности с Java и Angular2
У меня возникли проблемы с интеграцией spring безопасности (а именно с частью входа) в моем веб-приложении. My BackEnd работает на localhost: 8080, а FrontEnd (Ionic 2 с AngularJS 2) работает на localhost: 8100. Мне удалось войти (по крайней мере, я так думаю), но остальные запросы становятся недоступными, и я получаю следующую ошибку в консоли разработчика Chrome:
XMLHttpRequest cannot load http://localhost:8080/company/getAll. Response for preflight is invalid (redirect)
При тестировании с помощью Postman он работает, я вхожу в систему, а затем я могу выполнять POST-запросы на http://localhost:8080/company/getAll и все работает по назначению.
Предположительно, я пропускаю что-то (например, токен), но не могу понять, что. Я новичок в Ionic и spring Security, поэтому, пожалуйста, простите меня, если это что-то тривиальное. Я пробовал различные обучающие программы, но не смог ничего найти (большинство из них использовало JSP).
Как я могу заставить это работать? Как должны выглядеть мои запросы из интерфейса?
Вот мой метод входа с контроллера BackEnd:
@RequestMapping(value = "/login", method = RequestMethod.GET)
@CrossOrigin(origins = "*")
public ResponseEntity<GeneralResponse> login(Model model, String error, String logout) {
System.out.println("LOGIN"+model.toString());
if (error != null) {
System.out.println("1.IF");
model.addAttribute("error", "Your username and password is invalid.");
}
if (logout != null) {
System.out.println("2.IF");
model.addAttribute("message", "You have been logged out successfully.");
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new GeneralResponse(true, "FAILURE: Error"));
}
Это мой WebSecurityConfig:
@Configuration
@EnableWebSecurity
@ComponentScan("com.SAB.service")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/registration").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/user/login")
.permitAll()
.and()
.logout()
.permitAll();
http
.csrf().disable();
http.formLogin().defaultSuccessUrl("http://localhost:8100/", true);
//.and().exceptionHandling().accessDeniedPage("/403")
//.and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}
И, наконец, мой код FrontEnd, ответственный за вход в систему:
login(){
var body = 'username='+this.loginForm.controls['username'].value+'&password='+this.loginForm.controls['password'].value;
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
//headers.append("Access-Control-Allow-Origin", "*");
this.http
.post('http://localhost:8080/user/login',
body, {
headers: headers
})
.subscribe(data => {
console.log('ok');
console.log(data)
}, error => {
console.log(JSON.stringify(error.json()));
});
}
Если вам нужны какие-либо дополнительные данные, пожалуйста, сообщите мне, и я предоставлю их.
Спасибо заранее!
Я не получил ошибку Access-COntroll-Origin, но я попытался изменить ее в соответствии с комментариями в любом случае, и теперь я получаю "Недопустимый запрос CORS"
Заголовок запроса и ответа для входа от почтальона:
![введите описание изображения здесь]()
EDIT:
Вот журналы безопасности spring. Однако spring регистрирует только запрос OPTIONS и POST, даже если FrontEnd вызывает POST.
************************************************************
Request received for OPTIONS '/login':
[email protected]
servletPath:/login
pathInfo:null
headers:
host: localhost:8080
connection: keep-alive
access-control-request-method: POST
origin: http://localhost:8100
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
access-control-request-headers: access-control-allow-origin
accept: */*
referer: http://localhost:8100/
accept-encoding: gzip, deflate, sdch, br
accept-language: en-US,en;q=0.8
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /login at position 4 of 12 in additional filter chain; firing Filter: 'CorsFilter'
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.se[email protected]5baaaa2b
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
UPDATE:
Поэтому после добавления CORSFilter, предложенного в принятом ответе, мне также пришлось изменить мои запросы в интерфейсе, чтобы файл cookie с JSESSIONID отправлялся по каждому запросу. Баспически я должен был добавить следующее ко всем моим реквизитам:
let options = new RequestOptions({ headers: headers, withCredentials: true });
Ответы
Ответ 1
Это, похоже, проблема, связанная с конфигурацией CORS. Браузер делает запрос предварительной проверки OPTION перед вашим фактическим вызовом GET. У меня мало знаний в этой области, но вы можете попробовать добавить фильтр ниже в конец spring.
public class CORSFilter extends GenericFilterBean {
public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse response = (HttpServletResponse) res;
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.addHeader("Access-Control-Max-Age", "3600");
response.addHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
response.addHeader("Access-Control-Allow-Credentials", "true");
if(req.getMethod().equalsIgnoreCase("options")){
return;
}
chain.doFilter(request, response);
}
}
И добавьте строку ниже в метод настройки вашего WebSecurityConfig.
.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
Ответ 2
Решение очень простое. Имя пользователя и пароль в POST должны быть представлены как FormData, а не часть тела. У меня была такая же проблема с моим приложением, и после долгой борьбы я создал HTML-форму и сделал POST, тогда она работала как шарм.
См. ниже живой пример. Вы должны иметь возможность регистрироваться с любым идентификатором и пытаться войти в систему и дебютировать в своем браузере. Он использует загрузку Spring для серверной части и Angular 4 в пользовательском интерфейсе.
Live Demo: http://shop-venkatvp.rhcloud.com/#/
Вход в систему HTML: https://github.com/reflexdemon/shop/blob/master/src/app/login/login.component.html#L7-L31
Spring Конфигурация безопасности: https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/WebSecurityConfig.java#L20-L34
Контроллер входа: https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/page/controller/LoginController.java
Надеюсь, что это будет полезно.
Ответ 3
Вы пытались явно разрешить запросы OPTIONS? Вот так:
protected void configure(HttpSecurity http) throws Exception {
...
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
...
}
Ответ 4
Я нахожусь в середине написания статьи об интеграции Angular 4 и spring. У меня нет готовой статьи, но исходный код сделан. Я не уверен, почему вы пытаетесь обслуживать свое приложение с двух разных хостов, это излишне усложняет ваше приложение, введя требование cors и необходимость управлять двумя контекстами безопасности. Если это не абсолютное требование, я буду обслуживать все с одного и того же сервера.
Взгляните на этот проект github для полной интеграции Angular и spring boot: https://github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway. Вы можете игнорировать материал зуула там, если вам это не нужно. Это не влияет на поддержку Angular ui.
Если вы хотите включить csrf, и вы должны посмотреть на это сообщение: be4b206478c136d24ea1bf8b6f93da0f3d86a6c3
Если чтение, которое не имеет смысла, это шаги, которые вам нужно выполнить:
- Переместите исходный код Angular в ваше загрузочное приложение spring
- Измените файл angular -cli.json, чтобы иметь исходный каталог в ваших ресурсах static или resource public folder:
"outDir":"../../resources/static/home"
Это устанавливает выходной каталог, когда вы запускаете ng build из командной строки с помощью Angular кли.
- Измените URL-адрес успеха для входа в систему для перенаправления на /home/index.html
.formLogin().defaultSuccessUrl("/home/index.html", true)
, это заставит успешный логин всегда перенаправляться в ваше приложение Angular.
-
Если вы хотите автоматизировать создание своего приложения Angular в maven, то добавьте этот плагин в свой файл POM:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>generate-resources</phase>
<configuration>
<tasks>
<exec executable="cmd" osfamily="windows" dir="${project.basedir}/src/main/angular/ui">
<arg value="/c"/>
<arg value="ng"/>
<arg value="build"/>
</exec>
<exec executable="/bin/sh" osfamily="mac" dir="${project.basedir}/src/main/angular/ui">
<arg value="-c"/>
<arg value="ng build"/>
</exec>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Если у вас есть дополнительные вопросы, я буду рад ответить дальше, если этого недостаточно, чтобы вы его работали.
Ответ 5
Прежде всего - это, безусловно, проблема с настройкой CORS, поскольку вы используете бэкэнд и интерфейс на разных адресах.
Вы можете использовать backend для поддержки запросов CORS, но это грязное решение, espesialy, так как вы, вероятно, не хотите, чтобы CORS в вашей версии.
Гораздо более понятный подход состоял в том, чтобы настроить либо внешний интерфейс, либо сервер на сервере как прокси-сервер другому.
Я предпочитаю настроить интерфейс для прокси-запросов на бэкэнд. Таким образом, это не повлияет на производственную версию приложения.
В следующей части предполагается, что вы используете angular -cli (как и в случае с angular2).
У них есть раздел в своих документах специально для настройки прокси: https://www.npmjs.com/package/angular-cli#proxy-to-backend.
У меня есть все мои службы REST внутри/api-контекста, поэтому мой прокси .conf.json выглядит так:
{
"/api": {
"target": "http://localhost:8083",
"secure": false
},
"/login": {
"target": "http://localhost:8083",
"secure": false
},
"/logout": {
"target": "http://localhost:8083",
"secure": false
}
}
Где http://localhost:8083 - это мой бэкэнд. Затем вы можете запустить приложение:
ng serve --proxy-config proxy.conf.json
и используйте только свой внешний адрес. Конфигурация CORS не требуется.
P.S. Если вы не используете angular -cli, я уверен, что вы можете использовать этот подход с помощью любого инструмента, который вы используете для обслуживания frontent. До перехода на angular -cli я использовал это с lite-server.