hasRole始终返回403

时间:2017-04-30 09:33:41

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

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

除非我在antMatchers.requestMatchers()下复制.authorizeRequests(),否则我无法继续工作。我在这里显然遗漏了一些东西。

基本上我希望一切都要求身份验证,但是如果用户是某些组的成员(现在只是管理员),只有一些端点可以访问。

我的安全配置如下。 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, "/**");
    }
}

我的身份验证配置如下

@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

2 个答案:

答案 0 :(得分:0)

根据我对该问题的评论,我将提供我用于测试的示例OAuth2配置类。我总是使用两个不同的webapp,因为我想要在auth服务器和资源服务器之间划清界限(因为它使配置变得更加困难......),所以我的示例在单个webapp中使用时可能需要进行一些调整。

auth服务器的配置:

@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. 在资源服务器配置中指定requestMatchers()之前提供authorizeRequests()。根据处理配置的顺序和添加的过滤器链,可能需要这样才能在不需要OAuth令牌的情况下到达oauth端点。
  4. 编辑:通过ritesh.garg查看答案我认为我提供的内容可能无法解决您的问题,但可能有助于确定在何处以及如何开始配置Spring Security OAuth2(当我第一次找到它时这样做很难做到,因为当时我找不到任何明确的例子,虽然这可能已经改变了)

答案 1 :(得分:0)

我遇到了同样的问题,我只是忘记从 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());
}

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