使用 spring-boot OAuth2 服务器保护的 Spring-boot 应用程序

Posted

技术标签:

【中文标题】使用 spring-boot OAuth2 服务器保护的 Spring-boot 应用程序【英文标题】:Spring-boot application secured with a spring-boot OAuth2 server 【发布时间】:2015-11-30 16:38:21 【问题描述】:

我正在学习如何使用 Spring-boot 保护应用程序以及如何通过本教程设置 OAuth2 服务器:SSO with OAuth2: Angular JS and Spring Security Part V。如果我从referenced Github account 获取源代码,它可以工作,但不符合我的需要。

这是我想要的理想架构:

1) 得益于 nginx 重定向,前端(纯 html/js/css 与 angular & bootstrap)和后端之间的清晰分离。 nginx在80端口接收每一个请求。然后,如果它以/api开头,则重定向到8080端口。否则,它被nginx作为静态文件提供服务。

这里是对应的nginx conf:

server 
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /home/adenoyelle/IdeaProjects/myapp/frontend;
    index index.html index.htm;

    location /api 
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://127.0.0.1:8080;
    
 

这部分有效

2) 启用登录表单而不是使用基本身份验证对话框。如教程中所述,我创建了一个登录表单,然后在 Angular 一侧使用此选项来防止出现基本身份验证对话框:

$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';

自定义的“X-Requested-With”是浏览器客户端发送的常规标头,它曾经是 Angular 中的默认值,但在 1.3.0 中将其删除。 Spring Security 通过不在 401 响应中发送“WWW-Authenticate”标头来响应它,因此浏览器不会弹出身份验证对话框(这在我们的应用程序中是可取的,因为我们想要控制身份验证)。

但它不起作用,即每次我尝试访问受保护的资源或提交登录表单时都会出现基本身份验证弹出窗口!

在 chrome 控制台中,我可以看到请求标头中的 X-Requested-With:XMLHttpRequest 和 401 响应中的 WWW-Authenticate:Basic realm="Spring" 标头。

这是后端的相应代码:

@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class Backend 

  public static void main(String[] args) 
    SpringApplication.run(Backend.class, args);
  

  @Configuration
  protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter 

    @Override
    public void configure(HttpSecurity http) throws Exception 
      http.logout().and()
          .authorizeRequests()
          .antMatchers("/login").permitAll()
          .anyRequest().authenticated().and().csrf()
          .csrfTokenRepository(csrfTokenRepository()).and()
          .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
    

    private Filter csrfHeaderFilter() 
      return new 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);
        
      ;
    

    private CsrfTokenRepository csrfTokenRepository() 
      HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
      repository.setHeaderName("X-XSRF-TOKEN");
      return repository;
    
  


3) 我希望后端会收到登录请求,多亏了zuul,我希望它被转发到授权服务器,但也许我误解了这部分。

实际发生的情况:当我尝试提交登录表单时,我看到 Basic Authentication 弹出窗口,我将其关闭,然后我可以在后端服务器上看到一个跟踪:

15:21:13.664 [http-nio-8080-exec-6] INFO osbaaudit.listener.AuditListener - AuditEvent [timestamp=Fri Sep 04 15:21:13 CEST 2015, principal=user, type=AUTHENTICATION_FAILURE, data=type=org.springframework.security.authentication.BadCredentialsException, message=Bad credentials]

授权服务器上没有任何痕迹!

这是后端配置的相关部分:

spring.oauth2.client.access-token-uri=http://localhost:9999/uaa/oauth/token
spring.oauth2.client.user-authorization-uri=http://localhost:9999/uaa/oauth/authorize
spring.oauth2.client.client-id=acme
spring.oauth2.client.client-secret=acmesecret

zuul.routes.user.path=/user/**
zuul.routes.user.url=http://localhost:9999/uaa/user

有什么想法吗?

【问题讨论】:

这个东西你搞定了吗?如果可能,请分享您的源代码。谢谢 【参考方案1】:

禁用基本身份验证

@Override
public void configure(HttpSecurity http) throws Exception 
  http.logout()
      .and()
      .httpBasic().disable()
      .and()
      .authorizeRequests()
      .antMatchers("/login").permitAll()
      .anyRequest().authenticated().and().csrf()
      .csrfTokenRepository(csrfTokenRepository()).and()
      .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);

【讨论】:

【参考方案2】:

我不确定您的问题是否已解决。

由于项目 javascript 文件的 spring 授权,您正在弹出浏览器。如果您查看教程第 7 部分,则提到 js/** 文件夹中的内容默认情况下是不安全的。

注意:所有客户端代码都在一个目录“js”下(index.html 除外,因为那是一个“欢迎”页面并从“静态”目录自动加载)。这是有意的,因为它可以轻松地将单个 Spring Security 访问规则应用于所有静态资源。这些都是不安全的(因为 /js/** 在 Spring Boot 应用程序中默认是不安全的),但是您可能需要其他应用程序的其他规则,在这种情况下您将选择不同的路径。

如果您将文件放在 js/** 文件夹下,则不会出现浏览器弹出窗口。另一种方法是在 http configure 方法下在 antmatchers 中提及您的文件夹。

我希望这能解决问题。

谢谢。

【讨论】:

以上是关于使用 spring-boot OAuth2 服务器保护的 Spring-boot 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

如何使用spring-boot 1.3.0.RC1为oauth2提供自定义安全性配置

让 oauth2 与 spring-boot 和 rest 一起工作

将 OAuth2 添加到 spring-boot 时出现 CSRF-token 错误

使用 Twitch 进行身份验证时的 Spring-Boot OAuth2 奇怪行为

Spring-boot OAuth2 实现:NoSuchBeanDefinitionException:没有 AuthenticationManager 类型的合格 bean

微服务和 Spring Security OAuth2