はじめに、要件:
現在、Spring REST API と通信する AngularJS を使用してシングル ページ アプリケーションを作成しています。セキュリティ上の理由から、API へのすべてのリクエストをプロキシし、ユーザーが認証されていることを確認する zuul を使用してリバース プロキシをセットアップしたいと考えています。また、ユーザーが認証されていない場合は、OpenAM インスタンス (OAuth 2 承認サーバーとして機能する) にリダイレクトする必要があります。ユーザーが認証されている場合、少なくともユーザーの LDAP グループを含むヘッダーに Json Web トークン (JWT) を含むリクエストを API に転送する必要があります。
要するに、このチュートリアルのソリューションに似た API ゲートウェイのようなものが欲しいと思います: https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-セキュリティ-パート-v
現状
次の構成で Spring Cloud Security と Zuul をセットアップします。
server:
port: 9000
spring:
oauth2:
sso:
home:
secure: false
path: /,/**/*.html
client:
accessTokenUri: http://openam.example.org:8080/OpenAMTest/oauth2/access_token
userAuthorizationUri: http://openam.example.org:8080/OpenAMTest/oauth2/authorize
clientId: bearer-client
clientSecret: clientsecret
scope: openid profile
resource:
userInfoUri: http://openam.example.org:8080/OpenAMTest/oauth2/userinfo
zuul:
routes:
exampleApp:
path: /example-application/**
url: http://openam.example.org:8081/example-application
Application クラスは次のようになります。
@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class TestZuulProxy extends SpringBootServletInitializer {
public static void main(String[] args){
SpringApplication.run(TestZuulProxy.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<TestZuulProxy> applicationClass = TestZuulProxy.class;
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
@Override
public void match(RequestMatchers matchers) {
matchers.anyRequest();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/index.html", "/home.html", "/")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
public class CsrfHeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
}
}
「example-application」に移動すると、OpenAM 認証ログイン画面に転送されます。資格情報を入力すると、「example-application」にアクセスできます。ゲートウェイ サービスのコンソール ログ:
2015-06-22 17:14:10.911 INFO 6964 --- [nio-9000-exec-3] o.s.c.s.o.r.UserInfoTokenServices : Getting user info from: http://openam.example.org:8080/OpenAMTest/oauth2/userinfo
2015-06-22 17:14:10.953 INFO 6964 --- [nio-9000-exec-3] o.s.b.a.audit.listener.AuditListener : AuditEvent [timestamp=Mon Jun 22 17:14:10 CEST 2015, principal=Aaccf Amar, type=AUTHENTICATION_SUCCESS, data={details=remoteAddress=0:0:0:0:0:0:0:1, sessionId=<SESSION>, tokenType=BearertokenValue=<TOKEN>}]
Zuul フィルターによって読み取られる HTTP ヘッダー:
authorization --- Bearer c2b75b5a-c026-4e07-b8b9-81e9162c9277
x-forwarded-host --- localhost:9000
x-forwarded-prefix --- /example-application
だから何かがうまくいく!REST-API に転送されるアクセス トークンがあります。
問題
1) このソリューションは私の要件を実際には満たしていません。なぜなら、REST API で OpenAM のトークン エンドポイントを呼び出させたくないからです。必要なクレームを含む JWT がヘッダーの API に渡されるようにします。ゲートウェイ (Zuul フィルターなど) で JWT を手動で作成する必要がありますか、それとも別の解決策がありますか?
2) 上記のソリューションでは、アクセス トークンの有効期限が切れると、Zuul は引き続き API に転送します。どうしてこれなの?Spring Oauth2 はアクセストークンの有効期限が切れているかどうかをチェックしませんか? どうすればそれを実装できますか?
3) application.yml で tokenInfoUri も構成しようとしましたが、OpenAM が tokeninfo-Endpoint で GET 要求を予期していると思われるため、"405 Method Not Allowed" 例外が発生します。これをどうにかカスタマイズできますか?リクエストを変更するには、どのクラスをオーバーライド/カスタマイズする必要がありますか。
アドバイス、アイデア、解決策があれば教えてください!
ありがとうございました!