【问题标题】:Spring Oauth 2 SSO, Zuul and OpenAM integrationSpring Oauth 2 SSO、Zuul 和 OpenAM 集成
【发布时间】:2015-09-08 07:53:57
【问题描述】:

简介、要求:

现在我正在使用 AngularJS 编写一个与 Spring REST API 对话的单页应用程序。出于安全目的,我想使用 zuul 设置一个反向代理,它代理对 API 的每个请求并验证用户是否经过身份验证。此外,如果用户未通过身份验证,则应将他重定向到 OpenAM 实例(充当 OAuth 2 授权服务器)。如果用户已通过身份验证,则应将请求转发到 API,并在 Header 中使用 Json Web Token (JWT),至少包含用户的 LDAP 组。

简而言之,我想要一个类似于本教程中解决方案的 API 网关:https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-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。我应该手动在网关中创建 JWT(例如 Zuul 过滤器)还是有其他解决方案?

2) 在上述解决方案中,当访问令牌过期时,Zuul 不断将我转发到 API。为什么是这样? Spring Oauth2 不检查访问令牌是否过期?我该如何实现?

3) 我还尝试在 application.yml 中配置 tokenInfoUri,但随后出现“405 Method Not Allowed”异常,因为我认为 OpenAM 期望 tokeninfo-Endpoint 上的 GET 请求。我可以以某种方式自定义它吗?我需要重写/自定义哪些类来更改请求。

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

谢谢!

【问题讨论】:

  • 我还有一个你在第 3 点描述的问题 - 你解决了这个问题吗?每个“正常”的 oauth2 客户端应用程序都通过 POST 方法而不是 GET 请求 /tokeninfo 端点;/
  • 是的,如果我没记错的话,你必须实现 ResourceServerTokenServices 接口。 Spring 的默认实现 (RemoteTokenServices) 使用它们的 restTemplate 执行到 tokenInfoEndpoint 的 POST。我的 ResourceServerTokenServices 实现基本上看起来与 RemoteTokenServices impl 相同,但在 getForMap 方法中执行 HttpMethod.GET(在此处查找 RemoteTokenServices github.com/spring-projects/spring-security-oauth/blob/master/…

标签: oauth-2.0 openam spring-security-oauth2 spring-cloud netflix-zuul


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-18
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 2014-07-04
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    相关资源
    最近更新 更多