Spring Oauth 2 SSO, Zuul and OpenAM integration



简介,要求:

现在我正在用AngularJS编写一个单页应用程序,该应用程序与SpringRESTneneneba 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-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标头:

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 Filter)还是有其他解决方案?

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

3) 我也尝试在application.yml中配置tokenInfoUri,但后来我得到了一个"405方法不允许"的异常,因为我认为OpenAM期望在tokeninfoEndpoint上有一个GET请求。我可以以某种方式自定义它吗?我需要覆盖/自定义哪些类才能更改请求。

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

谢谢!

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

如果你想让网关在用户会话上强制执行AuthN/AuthZ,你可以使用ForgeLock的OpenIG。这可以作为策略执行点,并且能够内省JWT令牌。

最新更新