Spring 从数据库启动 Oauth2 UserDetailsS​​ervice - 访问被拒绝

Spring Boot Oauth2 UserDetailsService from database - access denied

我一直在尝试实现 Oauth2 授权和资源服务器。到目前为止,我一直在使用硬编码用户:

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public UserDetailsService userDetailsService() {

        UserDetails user=User.builder()
            .username("user")
            .password( passwordEncoder().encode("secret") )
            .roles("USER")
            .build();

        return new InMemoryUserDetailsManager(user);
    }

效果很好。但是我的数据库中有多个用户,我想将其用作用户登录。

因此,我从 class 中删除了上述 @Bean 方法,并将 UserDetailsS​​ervice 实现为它自己的 class:

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userRepository.findByUsername(username);
        System.out.println("loadUserByUsername: "+user);
        if (user == null) {
            throw new UsernameNotFoundException(username);
        }
        return new MyUserPrincipal(user);
    }
}

我的 UserDetails 实现:

public class MyUserPrincipal implements UserDetails {

    private static final long serialVersionUID = -1480402973442569981L;
    private User user;

    public MyUserPrincipal(User user) {
        this.user = user;
    }

    @Override
    public String getUsername() {
        System.out.println("Getting username: "+user.getUsername());
        return user.getUsername();
    }

    @Override
    public String getPassword() {
        System.out.println("Getting password: "+user.getPassword());
        return user.getPassword();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        final List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("USER"));
        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public User getUser() {
        return user;
    }

}

我的用户 table:

mysql> select * from user;
+----+--------------------------------------------------------------+----------+
| id | password                                                     | username |
+----+--------------------------------------------------------------+----------+
|  1 | atYTQ/dMXASLZG2OptweV.JdVH9RoDsG2ighxq5im3A/srnOo9OYC | user     |
+----+--------------------------------------------------------------+----------+
1 row in set (0.00 sec)

通过这个新设置,我可以像以前一样检索我的访问令牌:

POST /oauth/token

{
    "access_token": "8cf0a509-2a56-4adc-ace7-38f39beee8a1",
    "token_type": "bearer",
    "refresh_token": "742a109f-6768-48b5-9818-76397dc658fb",
    "expires_in": 42705,
    "scope": "read write"
}

但是,当我像以前一样尝试访问资源时,它现在给我以下响应:

获取/资金

{
    "error": "access_denied",
    "error_description": "Access is denied"
}

我没有看到任何错误或异常抛出。我怀疑这与角色有关,而这个用户只是没有被授权。这是我的 ClientDetailsS​​erviceConfigurer 设置:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    //...

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.inMemory()
            .withClient("cliente")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT", "USER")
            .scopes("read", "write")
            .autoApprove(true)
            .secret(passwordEncoder().encode("password"));

    }

更新

这是我的 ResourceServerConfigurerAdapter 实现:

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{
    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.anonymous().disable()
            .requestMatchers()
                .antMatchers("/funds/**")
            .and().authorizeRequests()
                .antMatchers("/funds/**")
                .access("hasRole('USER')")
            .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }   

}

我想在您的 ClientDetailsS​​erviceConfigurer 设置中,您需要提供 "ROLE_USER" 而不是 "USER" 的用户角色。 因为在内部(在用户中)所有角色都保存有前缀 ROLE_.

在用户class中(后缀已加):

authorities.add(new SimpleGrantedAuthority("ROLE_" + role));

Spring 安全性会自动为任何角色添加前缀 ROLE_。这些更改是 SEC-2758

的一部分

由于 SimpleGrantedAuthority 是权限,而不是角色,我们需要明确添加 ROLE_ 前缀以使其成为角色。

以下代码需要更新为 ROLE_ 前缀:

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    final List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
    return authorities;
}