[英]Spring Security login with Java and Angular2
我在我的Web應用程序中集成spring security(即登錄部分)時遇到了一些麻煩。 我的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進行測試時,它似乎正在工作,我登錄然后我能夠在http:// localhost:8080 / company / getAll上執行POST請求,一切都按預期工作。
據推測,我錯過了某些東西(比如令牌)但卻無法弄清楚是什么。 我是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請求”
編輯:這是Spring Security日志。 但是,即使FrontEnd正在調用POST,Spring也只注冊OPTIONS請求而不注冊POST。
************************************************************
Request received for OPTIONS '/login':
org.apache.catalina.connector.RequestFacade@18859793
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.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@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
更新:所以在添加了接受的答案中建議的CORSFilter之后,我還必須在前端修改我的請求,以便在每個請求上發送帶有JSESSIONID的cookie。 我不得不將以下內容添加到我的所有請求中: let options = new RequestOptions({ headers: headers, withCredentials: true });
這似乎是與CORS配置相關的問題。 瀏覽器確實會在您的實際GET調用之前發出OPTION預檢請求。 我對這方面的知識不多,但您可以嘗試將下面的過濾器添加到彈簧后端。
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的configure方法中添加以下行。
.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
解決方案非常簡單。 POST上的用戶名和密碼需要作為FormData提交,而不是正文的一部分。 我的應用程序遇到了同樣的問題,經過長時間的努力,我創建了一個HTML表單並進行了POST,然后它就像一個魅力。
見下面的實例。 您應該可以使用任何ID注冊並嘗試登錄並在瀏覽器中首次亮相。 它在服務器端使用Spring引導,在UI上使用Angular 4。
現場演示: 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
希望這是有幫助的。
我正在編寫Angular 4和spring boot集成文章。 我還沒有完成文章,但源代碼已完成。 我不確定您為什么要嘗試從兩個不同的主機提供應用程序,這會通過注入cors要求並且必須管理兩個安全上下文而不必要地使您的應用程序復雜化。 如果這不是絕對的要求,我會從同一台服務器上提供服務。
看一下這個github項目,完全集成角度和彈簧啟動: https : //github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway 。 如果你不需要,你可以忽略那里的zuul東西。 它對角度ui的支持沒有影響。
如果你想啟用csrf,你應該看看這個提交:be4b206478c136d24ea1bf8b6f93da0f3d86a6c3
如果閱讀沒有意義,這些是您需要遵循的步驟:
"outDir":"../../resources/static/home"
這將設置運行構建時的輸出目錄從命令行使用angular cli。 .formLogin().defaultSuccessUrl("/home/index.html", true)
這將強制成功登錄以始終重定向到您的角度應用程序。 如果要將角度應用程序自動構建為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>
如果您還有其他問題,我很樂意進一步回答,如果這還不足以讓您開始工作。
您是否嘗試明確允許OPTIONS請求? 像這樣:
protected void configure(HttpSecurity http) throws Exception {
...
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
...
}
首先 - 這絕對是一個CORS配置問題,因為您在不同的地址上運行后端和前端。
您可以使用后端來支持CORS請求,但這是一個骯臟的解決方案,尤其是因為您可能不希望在應用程序的生產版本中使用CORS 。 更簡潔的方法是將前端或后端配置為服務器作為另一個的代理。
我更喜歡將前端配置為代理后端請求。 這樣它對應用程序的生產版本沒有任何影響。
下一部分假設您正在使用angular-cli (正如您應該使用angular2 app)。
他們的文檔中有一節專門用於代理配置: https : //www.npmjs.com/package/angular-cli#proxy-to-backend 。
我在/ api上下文中有我所有的REST服務,所以我的proxy.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配置。
PS如果你不使用angular-cli我相信你可以使用這種方法用你用來提供前衛的任何工具。 在切換到angular-cli之前,我也使用了lite-server 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.