REST APIのSpring Security
1. 概要
このチュートリアルでは、Secure a REST API using Spring and Spring Security 5を実行する方法を学習します。
Java構成を使用してセキュリティを設定し、認証にログインとCookieのアプローチを使用します。
参考文献:
2. SpringSecurityを有効にする
The architecture of Spring Security is based entirely on Servlet Filters。 したがって、これはHTTPリクエストの処理に関してSpring MVCの前にあります。
Springセキュリティフィルターを登録する最も簡単なオプションは、構成クラスに@EnableWebSecurityアノテーションを付けることです。
@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
// ...
}
Spring Boot以外のアプリケーションの場合は、AbstractSecurityWebApplicationInitializerを拡張して、コンストラクターでconfigクラスを渡すことができます。
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityJavaConfig.class);
}
}
または、アプリケーションのweb.xmlで宣言できます。
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
コンテナ内でSpringSecurityによって作成されたデフォルトのBeanと一致するように、フィルタに‘springSecurityFilterChain'という名前を付ける必要があります。
定義されたフィルターは、セキュリティロジックを実装する実際のクラスではないことに注意してください。 代わりに、フィルターのメソッドを内部Beanに委任するのはDelegatingFilterProxyです。 これは、ターゲットBeanがSpringコンテキストのライフサイクルと柔軟性の恩恵を引き続き受けられるようにするために行われます。
Webサービス全体が/api/*にマップされている場合でも、フィルターの構成に使用されるURLパターンは/*です。 これにより、セキュリティ構成に、必要に応じて他の可能なマッピングも保護するオプションが提供されます。
3. Spring SecurityJava構成
WebSecurityConfigurerAdapterを拡張する構成クラスを作成し、それに@EnableWebSecurityアノテーションを付けることで、完全にJavaクラスでセキュリティ構成を行うことができます。
@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
// ...
}
それでは、APIエンドポイントの認証に使用するSecurityJavaConfigで異なるロールを持つユーザーを作成しましょう。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
.and()
.withUser("user").password(encoder().encode("userPass")).roles("USER");
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
次に、APIエンドポイントのセキュリティを構成しましょう。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/foos").authenticated()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.successHandler(mySuccessHandler)
.failureHandler(myFailureHandler)
.and()
.logout();
}
3.1. http要素
http要素は、セキュリティ構成の開始要素であり、セキュリティを構成するための流暢で柔軟な方法を提供します。
私たちの実装では、antMatchers. を使用して安全なマッピング/api/foos と/api/admin/** を作成しています
/api/foosパターンは、認証されたすべてのユーザーがアクセスできます。 一方、/api/admin/** には、ADMIN roleユーザーのみがアクセスできます。
3.2. エントリーポイント
標準のWebアプリケーションでは、認証されていないクライアントが保護されたリソースにアクセスしようとすると、認証プロセスが自動的にトリガーされます。 通常、このプロセスはログインページにリダイレクトされるため、ユーザーは資格情報を入力できます。
ただし、REST Webサービスの場合、この動作はあまり意味がありません。 We should be able to authenticate only by a request to the correct URI and if the user is not authenticated all requests should simply fail with a 401 UNAUTHORIZED status code.
Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point –これは構成の必須部分であり、authenticationEntryPoint メソッドを介して注入できます。
この機能はRESTサービスのコンテキストでは意味をなさないことに留意して、トリガーされたときに401を返すように新しいカスタムエントリポイントを定義します。
@Component
public final class RestAuthenticationEntryPoint
implements AuthenticationEntryPoint {
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized");
}
}
ここでの簡単な注意点は、HTTP仕様で要求されているように、401はWWW-Authenticateヘッダーなしで送信されるということです。 もちろん、必要な場合は手動で値を設定できます。
3.3. RESTのログインフォーム
REST APIの認証を行う方法は複数あります。 Spring Securityが提供するデフォルトの1つは、認証処理フィルターを使用するフォームログイン(org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter)です。
The formLogin element will create this filter and also provides additional methods successHandler and failureHandlerは、カスタム認証の成功ハンドラーと失敗ハンドラーをそれぞれ設定します。
標準のWebアプリケーションの場合、@EnableWebSecurityアノテーションは多くのデフォルト構成自体を構成することに注意してください。
3.4. 認証は301ではなく200を返す必要があります
デフォルトでは、フォームログインは成功した認証要求に301 MOVED PERMANENTLYステータスコードで応答します。これは、ログイン後にリダイレクトする必要がある実際のログインフォームのコンテキストでは意味があります。
ただし、for a RESTful web service, the desired response for a successful authentication should be 200 OK.
フォームログインフィルタのWe do this by injecting a custom authentication success handlerは、デフォルトのものを置き換えます。 新しいハンドラーは、デフォルトのorg.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandlerとまったく同じログインを実装しますが、注目すべき違いが1つあります。リダイレクトロジックが削除されます。
public class MySavedRequestAwareAuthenticationSuccessHandler
extends SimpleUrlAuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws ServletException, IOException {
SavedRequest savedRequest
= requestCache.getRequest(request, response);
if (savedRequest == null) {
clearAuthenticationAttributes(request);
return;
}
String targetUrlParam = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParam != null
&& StringUtils.hasText(request.getParameter(targetUrlParam)))) {
requestCache.removeRequest(request, response);
clearAuthenticationAttributes(request);
return;
}
clearAuthenticationAttributes(request);
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}
3.5. 失敗した認証は302ではなく401を返す必要があります
同様に-認証失敗ハンドラを構成しました-成功ハンドラで行ったのと同じ方法です。
幸いなことに、この場合、このハンドラーの新しいクラスを実際に定義する必要はありません。標準の実装であるSimpleUrlAuthenticationFailureHandlerは問題なく機能します。
3.6. 認証マネージャーとプロバイダー
認証プロセスは、in-memory providerを使用して認証を実行します。 これらの成果物の実稼働実装はこの記事の範囲外であるため、これは構成を簡素化することを目的としています。
user シェービングロールUSER とadmin withロールADMIN.の2人のユーザーを作成しました
3.7. 最後に–実行中のRESTサービスに対する認証
次に、さまざまなユーザーのRESTAPIに対して認証する方法を見てみましょう。
ログインのURLは/loginです。名前userとパスワードuserPassのユーザーのログインを実行する単純なcurlコマンドは次のようになります。
curl -i -X POST -d username=user -d password=userPass
http://localhost:8080/spring-security-rest/login
このリクエストは、RESTサービスに対する後続のリクエストに使用できるCookieを返します。
認証にcurlを使用し、store the cookie it receives in a fileを使用できます。
curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login
次に、we can use the cookie from the fileを使用して、さらに認証された要求を実行します。
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/foos
user canは/api/foos/ endpointにアクセスするため、この認証された要求は正しく*結果として200OKになります。
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 24 Jul 2013 20:31:13 GMT
[{"id":0,"name":"JbidXc"}]
同様に、admin userの場合、認証にcurlを使用できます。
curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login
次に、管理エンドポイントにアクセスするためのCookieを更新しました/api/admin/*:
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/admin/x
admin userはエンドポイント/api/admin/* にアクセスできるため、これにより成功応答が返されます。
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 5
Date: Mon, 15 Oct 2018 17:16:39 GMT
Hello
4. XMLセキュリティ構成
Java構成の代わりにXMLを使用して、上記のすべてのセキュリティ構成を行うこともできます。
ほとんどの構成は、セキュリティ名前空間を使用して行われます。 これを有効にするには、スキーマの場所を定義する必要があります。
名前空間は、Spring Securityの一般的なユースケースを表現しながら、より高度なシナリオに対応するためにraw Beanのフックを提供するように設計されています。
5. 結論
このチュートリアルでは、Spring Security 5を使用したRESTfulサービスの基本的なセキュリティ設定と実装について説明しました。
完全にJavaconfigを介してRESTAPIのセキュリティ構成を行う方法を学び、そのweb.xml構成の代替案も検討しました。
次に、セキュリティで保護されたアプリケーションのユーザーとロールを作成する方法と、これらのユーザーをアプリケーションの特定のエンドポイントにマップする方法について説明しました。
最後に、セキュリティを制御するという点でアプリケーションの柔軟性を高めるカスタム認証エントリポイントとカスタム成功ハンドラの作成も検討しました。
完全な実装はover on Githubで利用できます。