【问题标题】:Custom http security configuration along with OAuth2 resource server自定义 http 安全配置以及 OAuth2 资源服务器
【发布时间】:2016-07-14 00:49:09
【问题描述】:

我正在使用 Spring Security OAuth2 和一个运行良好的非常基本的配置。我现在想要一个单独的WebSecurityConfigurerAdapter,其中包含确定某人是否有权访问某些端点的自定义逻辑。但是,无论我尝试什么,它都不会执行。以下是我的OAuth2 配置和我对该主题的发现。授权服务器:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private AuthenticationManagerBuilder authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {
        endpoints.authenticationManager(authentication -> authenticationManager.getOrBuild().authenticate(authentication)).tokenStore(tokenStore);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("CLIENT_NAME")...;
    }

}

资源服务器:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
    }

    @Override
    public void configure(final ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore);
    }
}

到目前为止一切顺利。但是,当自定义 WebSecurityConfigurerAdapter 开始起作用时,我开始遇到问题。由于EnableResourceServer-annotated bean 创建了一个带有Order(3)WebSecurityConfigurerAdapter,它首先在每个请求上执行,用户已成功验证/授权,但我的WebSecurityConfiguration 中的自定义逻辑未执行。另一方面,如果我将WebSecurityConfiguration 设置为Order(2) 或更少,则会执行自定义access 规则,但它总是说它们来自匿名用户(因为@ 创建的bean 中的规则987654334@ 未执行)。

@EnableWebSecurity
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/...");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeRequests().antMatchers(HttpMethod.GET, "/path/**")
        .access("@security.hasPermission(authentication, 'SOME', 'VALUE')");

        http.authorizeRequests().anyRequest().authenticated();
    }
}

顺便说一句,access 规则中的 @security 引用只是一个简单的命名 Spring bean:

@Component("security")
public class SecurityService {
    public boolean hasPermission(Authentication authentication, String param, String anotherParam) { ... }
}

我有集成测试来验证WebSecurityConfiguration 中的自定义访问规则并且它们有效(因为我跳过了那里的身份验证)。我希望能够将资源服务器仅用于身份验证,然后将我的自定义 http 安全性用于授权。

【问题讨论】:

    标签: spring spring-security spring-boot spring-security-oauth2


    【解决方案1】:

    上述代码中有两件事,首先,您应该为客户端分配一些权限。

    这里有一些代码:

    在 OAuth2AuthorizationServerConfig 中你应该改变它

    clients.inMemory().withClient("CLIENT_NAME").authorities("ADMIN")....;
    

    在匹配器中你应该这样做

     http.authorizeRequests().antMatchers(HttpMethod.GET, "/path/**")
        .hasAuthority("ADMIN");
    

    或者您可以为安全方法/控制器添加注释

     @PreAuthorize("hasAuthority('ADMIN')")
    

    【讨论】:

      【解决方案2】:

      假设您想使用 oauth2,并且您首先获得一个令牌并在您的 API 中使用它(使用 Authorization Bearer [TOKEN]),您需要稍微更改您的逻辑以创建自定义表达式。

      首先,如果你使用spring boot 1.5+,请考虑将以下属性添加到你的application.properties文件中:security.oauth2.resource.filter-order=3(1.5 release notes)

      现在,为了创建自定义过滤器,您需要了解一些组件。

      默认情况下,spring 提供以下接口MethodSecurityExpressionHandler 来处理安全表达式,使用的实现是DefaultMethodSecurityExpressionHandler。通常你会用OAuth2MethodSecurityExpressionHandler 覆盖它,但让我们用我们自己的实现创建一个自定义的。

      该类使用SecurityExpressionRoot实现的接口MethodSecurityExpressionOperations来解析表达式。

      首先,我们需要创建一个自定义MethodSecurityExpressionOperations。在它的底部,您可以找到 // custom logic methods 说明自定义逻辑的开始(在它之上是 spring 的实现以保持与其表达式的兼容性):

      public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
      
          // Based on MethodSecurityExpressionRoot (class is package private in spring)
          private Object filterObject;
          private Object returnObject;
          private Object target;
      
          CustomMethodSecurityExpressionRoot(Authentication a) {
              super(a);
          }
      
          public void setFilterObject(Object filterObject) {
              this.filterObject = filterObject;
          }
      
          public Object getFilterObject() {
              return filterObject;
          }
      
          public void setReturnObject(Object returnObject) {
              this.returnObject = returnObject;
          }
      
          public Object getReturnObject() {
              return returnObject;
          }
      
          /**
           * Sets the "this" property for use in expressions. Typically this will be
           * the "this" property of the {@code JoinPoint} representing the method
           * invocation which is being protected.
           *
           * @param target
           *            the target object on which the method in is being invoked.
           */
          void setThis(Object target) {
              this.target = target;
          }
      
          public Object getThis() {
              return target;
          }
      
          // custom logic methods
          public boolean securityHasPermission(String param, String anotherParam) {
              /* custom logic here */
          }
      

      接下来,我们需要在自定义MethodSecurityExpressionHandler中将其设置为我们的根:

      public class CustomOAuth2MethodSecurityExpressionHandler extends OAuth2MethodSecurityExpressionHandler {
      
          private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
      
          @Override
          protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
                  MethodInvocation invocation) {
              final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
              root.setThis(invocation.getThis());
              root.setPermissionEvaluator(getPermissionEvaluator());
              root.setTrustResolver(this.trustResolver);
              root.setRoleHierarchy(getRoleHierarchy());
      
              return root;
          }
      
      }
      

      接下来,我们需要将其设置为默认的MethodSecurityExpressionHandler。我们可以通过扩展GlobalMethodSecurityConfiguration 来做到这一点。在该文件中,我们为自定义 MethodSecurityExpressionHandler 定义一个新的 @Bean 并覆盖 createExpressionHandler() 以将其设置为默认值:

      @Configuration
      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class Oauth2GlobalMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
      
          @Override
          protected MethodSecurityExpressionHandler createExpressionHandler() {
              return methodSecurityExpressionHandler();
          }
      
          @Bean
          public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
              return new CustomOAuth2MethodSecurityExpressionHandler();
          }
      

      现在您可以在控制器中添加@PreAuthorize("securityHasPermission('SOME', 'VALUE')") 来触发您的自定义逻辑

      您的自定义逻辑应该在CustomMethodSecurityExpressionRoot 中触发,并且您可以访问authentication 变量,因为它在初始化时已经注入到类中。在CustomOAuth2MethodSecurityExpressionHandler 中设置时,您还可以传递更多参数/bean。

      【讨论】:

      • PreAuth 与 OAuth 完全不同,不是吗?其实很好奇。我正在使用 preAuth 并被告知将其切换到 oauth,因为我正在使用的令牌在它到达我的应用程序之前没有经过预先身份验证,即使您在收到未经身份验证的令牌后调用经过身份验证的令牌,利用率也是据我所知,技术上仍然不是预授权。
      【解决方案3】:

      您可以将 OAuth2MethodSecurityExpressionHandler 实例生成为 bean 来解决此问题。

      改为这样做:

      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class OAuth2ResourceServerConfig extends GlobalMethodSecurityConfiguration {
      
              @Override
              protected MethodSecurityExpressionHandler createExpressionHandler() {
                   return new OAuth2MethodSecurityExpressionHandler();
              }
      
              ....
      }
      

      这样做:

      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class OAuth2ResourceServerConfig extends GlobalMethodSecurityConfiguration {
      
              @Override
              protected MethodSecurityExpressionHandler createExpressionHandler() {
                   return getOAuth2MethodSecurityExpressionHandler();
              }
      
              @Bean
              public OAuth2MethodSecurityExpressionHandler getOAuth2MethodSecurityExpressionHandler() {
                   return new OAuth2MethodSecurityExpressionHandler();
              }
      
              ....
      }
      

      希望这会是其他人!

      【讨论】:

        猜你喜欢
        • 2016-01-18
        • 2017-09-13
        • 2017-03-25
        • 2021-06-03
        • 2018-05-17
        • 2015-06-13
        • 2016-04-22
        • 2015-11-19
        • 2021-02-13
        相关资源
        最近更新 更多