【问题标题】:hasRole always return 403hasRole 总是返回 403
【发布时间】:2017-09-28 00:47:38
【问题描述】:

我似乎无法正确配置我的安全配置。无论我在使用 hasRole 时做什么,我的端点总是返回 403。

除非我在.requestMatchers() 和.authorizeRequests() 下复制我的antMatchers,否则我什么也做不了。我显然在这里遗漏了一些东西。

基本上,我希望所有内容都需要身份验证,但只有当用户是某些组的成员(现在只是管理员)时,才能访问一些端点。

我的安全配置如下。 hasRole 之外的所有内容都可以正常工作。

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .requestMatchers()
                .antMatchers(HttpMethod.GET, "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html")
                .antMatchers(HttpMethod.GET, "/users")
                .and()
            .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html").permitAll()
                .antMatchers(HttpMethod.GET, "/users").hasRole("ADMIN")    
                .anyRequest().authenticated();
    }

    // Inspiration: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#comment-2416096114
    @Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
                .antMatchers(HttpMethod.OPTIONS, "/**");
    }
}

我的AuthenticationConfiguration如下

@Configuration
@EnableResourceServer
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
    private final UserDetailsService userService;
    private final PasswordEncoder passwordEncoder;

    public AuthenticationConfiguration(UserDetailsService userService, PasswordEncoder passwordEncoder) {
        this.userService = userService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userService)
                .passwordEncoder(passwordEncoder);
    }
}

我的 AuthorizationServerConfiguration 如下

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    private final AuthenticationManager authenticationManager;

    public AuthorizationServerConfiguration(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient("html5")
                .secret("password")
                .authorizedGrantTypes("password")
                .scopes("openid");
    }
}

我很乐意发布我的用户服务和其他内容。但是除了hasRole 和Principal 之外,一切似乎都正常工作,并加载了正确的权限(角色)。但是请让我知道我是否应该发布更多代码。

完整的源代码可以在here找到。

【问题讨论】:

  • @dur - 是的,但我觉得很难理解......显然。
  • @M.Deinum - 我的权限以 ROLE_ 为前缀
  • 我正在执行以下命令:http localhost:8080/users "Authorization: Bearer $TOKEN"。客户端称为 httpie,但 curl 或 postman... 一样一样。
  • @dur - gist.github.com/tonsV2/fec750c0f5a26a857caf49659df6dcd6 - 这就是输出。我所收集的只是它被拒绝了。不知道为什么。唯一奇怪的是它说用户是匿名的。
  • @dur - 这很奇怪。主体已填充,并且仅当我将不记名令牌传递到其他地方时才授予访问权限。无论如何,我已经用请求的代码更新了问题。

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


【解决方案1】:

您是否尝试过使用“ROLE_ADMIN”而不仅仅是“ADMIN”?看看这个以供参考:

Spring security added prefix "ROLE_" to all roles name?

【讨论】:

    【解决方案2】:

    跟进我的 cmets 问题,我将提供我用于测试的示例 OAuth2 配置类。我总是使用两个不同的 webapp,因为我希望在身份验证服务器和资源服务器之间有一条清晰的界限(并且因为它使配置变得更加困难......),所以我的示例在单个 webapp 中使用时可能需要进行一些调整。

    认证服务器的配置:

    @EnableAuthorizationServer
    @Configuration
    public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    
        private TokenStore tokenStore;
        private DataSource dataSource;
    
        @Autowired
        public OAuth2Config(TokenStore tokenStore,
                            DataSource dataSource) {
            this.tokenStore = tokenStore;
            this.dataSource = dataSource;
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.jdbc(dataSource);
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(tokenStore);
        }
    
    
        @Configuration
        public static class TokenStoreConfiguration {
            @Bean
            public TokenStore tokenStore(DataSource dataSource) {
                return new JdbcTokenStore(dataSource);
            }
        }
    }
    

    资源服务器配置:

    @EnableResourceServer
    @Configuration
    public class OAuth2Config extends ResourceServerConfigurerAdapter {
        public static final String PROPERTY_RESOURCE_ID = "com.test.oauth.resourceId";
    
        private Environment environment;
        private TokenStore tokenStore;
    
        @Autowired
        public OAuth2Config(Environment environment,
                            TokenStore tokenStore) {
            this.environment = environment;
            this.tokenStore = tokenStore;
        }
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenStore(tokenStore)
                    .resourceId(environment.getProperty(PROPERTY_RESOURCE_ID))
                    .stateless(true);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/demo")
                        .access("hasRole('DEMO')")
    
                    .anyRequest().denyAll()
                    .and()
                    .formLogin().disable()
                    .logout().disable()
                    .jee().disable()
                    .x509().disable();
        }
    
        @Configuration
        public static class TokenStoreConfiguration {
            @Bean
            public TokenStore tokenStore(DataSource dataSource) {
                return new JdbcTokenStore(dataSource);
            }
        }
    }
    

    显然,这需要您配置 DataSource bean。此实现使用 spring security OAuth2 提供的默认表(它们远非理想,但可以根据需要进行自定义)。

    您可能需要针对您的情况进行一些调整(如果人们可能希望将其与 JDBC 一起使用,我将保留我提供的类以供参考):

    1. 只创建一个TokenStore 类型的bean 并使用InMemoryTokenStore 而不是JdbcTokenStore
    2. 用您的inMemory() 实现替换客户端的配置,并删除对我的自动连线DataSource 的所有引用
    3. 在资源服务器配置中指定authorizeRequests() 之前提供requestMatchers()。根据处理配置和添加过滤器链的顺序,这可能需要允许到达 oauth 端点而不需要 OAuth 令牌。

    编辑:看到 ritesh.garg 的答案我认为我提供的内容可能无法解决您的问题,但可能有助于弄清楚从何处以及如何开始配置 Spring Security OAuth2(当我第一次找到它时)很难做到,因为当时我找不到任何明确的例子,尽管这可能已经改变了)

    【讨论】:

      【解决方案3】:

      我有同样的问题,我只是忘了从 UserDetails(SpringSecurity 类)实现 getAuthorities() 方法。看看我的实体:

      import org.springframework.security.core.GrantedAuthority;
      import org.springframework.security.core.authority.SimpleGrantedAuthority;
      import org.springframework.security.core.userdetails.UserDetails;
      
      import javax.persistence.*;
      import java.io.Serializable;
      import java.util.Collection;
      import java.util.HashSet;
      import java.util.Objects;
      import java.util.Set;
      import java.util.stream.Collectors;
      
      @Entity
      @Table(name = "tb_user")
      public class User implements UserDetails, Serializable {
      
      private static final long serialVersionUID = -6519124777839966091L;
      
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
      private String firstName;
      private String lastName;
      
      @Column(unique = true)
      private String email;
      private String password;
      
      @ManyToMany(fetch = FetchType.EAGER)
      @JoinTable(
              name = "tb_user_role",
              joinColumns = @JoinColumn(name = "user_id"),
              inverseJoinColumns = @JoinColumn(name = "role_id")
      )
      private Set<Role> roles = new HashSet<>();
      
      public User() {
      }
      
      public User(Long id, String firstName, String lastName, String email, String password) {
          this.id = id;
          this.firstName = firstName;
          this.lastName = lastName;
          this.email = email;
          this.password = password;
      }
      
      public Long getId() {
          return id;
      }
      
      public void setId(Long id) {
          this.id = id;
      }
      
      public String getFirstName() {
          return firstName;
      }
      
      public void setFirstName(String firstName) {
          this.firstName = firstName;
      }
      
      public String getLastName() {
          return lastName;
      }
      
      public void setLastName(String lastName) {
          this.lastName = lastName;
      }
      
      public String getEmail() {
          return email;
      }
      
      public void setEmail(String email) {
          this.email = email;
      }
      
      public void setPassword(String password) {
          this.password = password;
      }
      
      public Set<Role> getRoles() {
          return roles;
      }
      
      @Override
      public Collection<? extends GrantedAuthority> getAuthorities() {
          return roles.stream().map(role -> new SimpleGrantedAuthority(role.getAuthority()))
                  .collect(Collectors.toList());
      }
      
      public String getPassword() {
          return password;
      }
      
      @Override
      public String getUsername() {
          return email;
      }
      
      @Override
      public boolean isAccountNonExpired() {
          return true;
      }
      
      @Override
      public boolean isAccountNonLocked() {
          return true;
      }
      
      @Override
      public boolean isCredentialsNonExpired() {
          return true;
      }
      
      @Override
      public boolean isEnabled() {
          return true;
      }
      
      
      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
          User user = (User) o;
          return Objects.equals(id, user.id);
      }
      
      @Override
      public int hashCode() {
          return Objects.hash(id);
      }
      }
      

      当你从安全包中扩展 UserDetails 类时,getAuthorities 方法默认返回 null,你需要实现类似的东西:

      @Override
      public Collection<? extends GrantedAuthority> getAuthorities() {
          return roles.stream().map(role -> new SimpleGrantedAuthority(role.getAuthority()))
              .collect(Collectors.toList());
      }
      

      我希望这对某人有所帮助,对我的英语错误感到抱歉!呵呵

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-23
        • 2018-08-15
        • 2021-03-23
        • 2015-10-23
        • 2018-08-28
        • 1970-01-01
        • 2021-06-27
        相关资源
        最近更新 更多