2

简介、要求:

现在我正在用 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 请求。我可以以某种方式自定义它吗?我需要覆盖/自定义哪些类来更改请求。

如果您有建议、想法或可能的解决方案,请告诉我!

谢谢!

4

1 回答 1

0

如果您想在应用程序中使用 JWT,请将 OpenAM 配置为 OpenID Connect 提供程序(OpenAM 12.0 或更高版本)。一旦用户通过身份验证,OpenAM 将发出一个 JWT,其中包含有关用户的许多声明。您的 SPA 可以将请求传递给您的服务层。

如果您希望网关在用户会话中强制执行 AuthN/AuthZ,您可以使用 ForgeRock 的 OpenIG 之类的东西。这可以作为策略执行点,并具有内省 JWT 令牌的能力。

于 2015-06-23T13:29:33.163 回答