Springセキュリティ(つまりログイン部分)をWebアプリケーションに統合する際に問題が発生します。私のバックエンドはlocalhost:8080で実行されており、フロントエンド(Ionic 2 with AngularJS 2)はlocalhostで実行されています: 8100。私はなんとかログインできました(少なくともそう思う)が、残りのリクエストは利用できなくなり、Chrome Developer Console:
_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());
}
}
_
最後に、ログインを担当するフロントエンドコード:
_ 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リクエスト」が表示されます。
Postmanからのログインのリクエストおよびレスポンスヘッダー:
EDIT:これがSpring Securityログです。ただし、SpringはOPTIONSリクエストのみを登録し、FrontEndがPOSTを呼び出している場合でも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
_
UPDATE:したがって、受け入れられた回答で提案された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
これがお役に立てば幸いです。
Angular 4とSpring Bootの統合に関する記事を書いている最中です。記事はまだ完成していませんが、ソースコードは完成しています。なぜだかわかりません2つの異なるホストからアプリケーションにサービスを提供しようとすると、cors要件を挿入し、2つのセキュリティコンテキストを管理する必要があるため、アプリケーションが不必要に複雑になります。これが絶対的な要件でない場合は、同じサーバーからすべてを提供します。
angularとスプリングブートの完全な統合については、このgithubプロジェクトをご覧ください。 https://github.com/tschiman/tutorials/tree/master/spring-cloud/ spring-cloud-bootstrap/gateway 。必要がない場合は、そこのzuulを無視できます。angular uiのサポートに影響はありません。
Csrfを有効にする必要がある場合は、次のコミットを確認してください:be4b206478c136d24ea1bf8b6f93da0f3d86a6c3
それを読んでも意味がない場合は、次の手順に従ってください。
"outDir":"../../resources/static/home"
_これは、angular cli。.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>
_
他にご不明な点がありましたら、それでも十分に機能しなかった場合は、喜んで回答させていただきます。
まず第一に-バックエンドとフロントエンドを異なるアドレスで実行しているため、これは間違いなくCORS構成の問題です。
CORSリクエストをサポートするためにバックエンドを切り替えることができますが、これはダーティソリューションです。おそらく、アプリの本番環境バージョンでCORSを望まないためです。よりクリーンなアプローチは、フロントエンドまたはバックエンドのいずれかをサーバーのプロキシとしてサーバーに設定することです。
リクエストをバックエンドにプロキシするようにフロントエンドを構成することを好みます。この方法では、アプリの製品版には影響しません。
次の部分では、angular-cliを使用していることを前提としています(angular2アプリの場合と同様)。
彼らはドキュメントに特にプロキシ設定のためのセクションを持っています: https://www.npmjs.com/package/angular-cli#proxy-to-backend 。
すべてのRESTサービスが/ apiコンテキスト内にあるため、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:808 は私のバックエンドです。次に、以下を使用してアプリを実行できます。
ng serve --proxy-config proxy.conf.json
フロントエンドアドレスのみを使用します。 CORS設定は必要ありません。
追伸angular-cliを使用しない場合は、フロントエンドの提供に使用するツールでこのアプローチを使用できることを確認してください。 angular-cliに切り替える前に、これを lite-server でも使用しました。
OPTIONSリクエストを明示的に許可しようとしましたか?このような:
protected void configure(HttpSecurity http) throws Exception {
...
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
...
}