简介、要求:
现在我正在用 AngularJS 编写一个与 Spring REST API 对话的单页应用程序。出于安全目的,我想使用 zuul 设置一个反向代理,它代理对 API 的每个请求并验证用户是否经过身份验证。此外,如果用户未通过身份验证,则应将他重定向到 OpenAM 实例(充当 OAuth 2 授权服务器)。如果用户已通过身份验证,则应将请求转发到 API,并在标头中使用 Json Web 令牌 (JWT),其中至少包含用户的 LDAP 组。
简而言之,我想要一个类似于本教程中解决方案的 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);
}
}
}
}
现在,当我转到“示例应用程序”时,我会被转发到 OpenAM 授权登录屏幕。当我输入凭据时,我可以访问“示例应用程序”。网关服务上的控制台日志:
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-Header:
authorization --- Bearer c2b75b5a-c026-4e07-b8b9-81e9162c9277
x-forwarded-host --- localhost:9000
x-forwarded-prefix --- /example-application
所以有些东西起作用了!我有一个被转发到 REST-API 的访问令牌。
问题
1)这个解决方案并不能真正满足我的要求,因为我不希望 REST API 调用 OpenAM 的令牌端点。我希望将带有必要声明的 JWT 传递给 Header 中的 API。我应该在网关(例如 Zuul 过滤器)中手动创建 JWT,还是有其他解决方案?
2)在上面的解决方案中,当访问令牌过期时,Zuul 不断将我转发到 API。为什么是这样?Spring Oauth2 不检查访问令牌是否过期?我该如何实施?
3) 我还尝试在 application.yml 中配置 tokenInfoUri,但随后出现“405 Method Not Allowed”异常,因为我认为 OpenAM 需要对 tokeninfo-Endpoint 的 GET 请求。我可以以某种方式自定义它吗?我需要覆盖/自定义哪些类来更改请求。
如果您有建议、想法或可能的解决方案,请告诉我!
谢谢!