Spring 拒绝访问数据由 oauth2 授权

Access Denied with Spring data rest with oauth2 authorization

我卡在这个授权问题上了,怎么办都无法转发。

如果我使用@PreAuthorize 保护我的资源,我会收到 access_denied 响应。

其余控制器:

@RestController
@RequestMapping("/api/users")
public class UserRestController {
private UserService userService;

    @GetMapping
    @PreAuthorize("hasRole('USER')")
    public ResponseEntity<Collection<User>> findAll() {
        return ResponseEntity.status(HttpStatus.OK).body(userService.findAll());
    }
}

网络安全:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin().disable() // disable form authentication
                .anonymous().disable() // disable anonymous user
                .authorizeRequests().anyRequest().denyAll(); // denying all access
    }
}

授权服务器配置:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private ApplicationConfigurationProperties configuration;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailOath2Service userDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .tokenStore(tokenStore())
                .tokenServices(tokenServices())
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient(configuration.getClientId())
                .secret(configuration.getClientSecret())
                .scopes("read", "write")               
                .authorizedGrantTypes("client_credentials", "password", "refresh_token")
                .resourceIds(RestApiResourceServerConfiguration.RESOURCE_ID);
    }


    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setTokenEnhancer(accessTokenConverter());
        return defaultTokenServices;
    }

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

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("abcd");
        return converter;
    }

}

资源服务器:

@Configuration
@EnableResourceServer
public class RestApiResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    public static final String RESOURCE_ID = "restservice";

    @Autowired
    private DefaultTokenServices tokenServices;

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID)
                .tokenServices(tokenServices)
                .tokenStore(tokenStore);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .anyRequest().permitAll().and().csrf().disable();
    }

}

用户详情服务:

@Service
public class UserDetailOath2Service implements UserDetailsService {

    private final Logger LOGGER = Logger.getLogger(UserDetailOath2Service.class);

    @Autowired
    private UserRepository repository;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) {
        LOGGER.info("Entering in loadUserByUsername " + username);
        final User user = repository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        final List<SimpleGrantedAuthority> authorities = user.getAuthorities().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
    }

当我请求 http://localhost:8090/oauth/token 时,我收到了令牌:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzdHNlcnZpY2UiXSwidXNlcl9uYW1lIjoiYWxlcyIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1NDgxMTExNjcsImF1dGhvcml0aWVzIjpbIlVTRVIiXSwianRpIjoiNGZjODNiOTktMjZiNC00NWZkLWIxMGQtZDgxMzAzZDM2MjM4IiwiY2xpZW50X2lkIjoiZGF0YXJlc3RjbGllbnQifQ.rZAB_LmKuAN6R7i-7dUvYv4Q6vr8LhTNKgPMDVufFTc",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzdHNlcnZpY2UiXSwidXNlcl9uYW1lIjoiYWxlcyIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI0ZmM4M2I5OS0yNmI0LTQ1ZmQtYjEwZC1kODEzMDNkMzYyMzgiLCJleHAiOjE1NTA2NTk5NjcsImF1dGhvcml0aWVzIjpbIlVTRVIiXSwianRpIjoiNGNiYWYyZWUtOTFhOC00N2Q2LTllZmEtYzA4ODI1NTI5MmQ3IiwiY2xpZW50X2lkIjoiZGF0YXJlc3RjbGllbnQifQ.41tdJ3Qc4nodc4ZAOr6dhYOa8XTqBOFQc9X1yM7NrGE",
    "expires_in": 43199,
    "scope": "read write",
    "jti": "4fc83b99-26b4-45fd-b10d-d81303d36238"
}

所以我拿到令牌并尝试调用受保护的资源:

GET /api/users HTTP/1.1
Host: localhost:8090
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzdHNlcnZpY2UiXSwidXNlcl9uYW1lIjoiYWxlcyIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1NDgxMTExNjcsImF1dGhvcml0aWVzIjpbIlVTRVIiXSwianRpIjoiNGZjODNiOTktMjZiNC00NWZkLWIxMGQtZDgxMzAzZDM2MjM4IiwiY2xpZW50X2lkIjoiZGF0YXJlc3RjbGllbnQifQ.rZAB_LmKuAN6R7i-7dUvYv4Q6vr8LhTNKgPMDVufFTc

结果是这样的:

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

我生成令牌的用户具有 USER 角色。

拜托,有人可以帮我找出我做错了什么吗?

谢谢。

生成的令牌具有 USER authority,而不是 USER role。 角色和权限之间存在细微但重要的区别:https://www.baeldung.com/spring-security-granted-authority-vs-role

@PreAuthorize("hasRole('USER')")更改为@PreAuthorize("hasAuthority('USER')")或授予权限ROLE_USER以获得角色USER

您似乎没有为该角色提供资源访问权限。

public void configure(final HttpSecurity http) throws Exception {
    // @formatter:off
    http.csrf().disable().authorizeRequests()
    // This is needed to enable swagger-ui interface.
    .antMatchers("/swagger-ui.html","/swagger-resources/**","/webjars/**", "/v2/api-docs/**").permitAll()
    .antMatchers("/api/v1/**").hasAuthority("ROLE_TRUSTED_CLIENT")
    // @formatter:on
  }

确保您没有覆盖其他 role/permission 的权限。所以配置像

public void configure(final HttpSecurity http) throws Exception {
    // @formatter:off
    http.csrf().disable().authorizeRequests()
    // This is needed to enable swagger-ui interface.
    .antMatchers("/swagger-ui.html","/swagger-resources/**","/webjars/**", "/v2/api-docs/**").permitAll()
    .antMatchers("/api/v1/**").hasAuthority("ROLE_TRUSTED_CLIENT")
    .antMatchers("/api/v1/**").hasAuthority("ROLE_USER");
    // @formatter:on
  }

会有问题。该权限现在仅授予 ROLE_USER 和 ROLE_TRUSTED_CLIENT。

要提供多个角色访问权限,请使用以下内容

http.csrf().disable().authorizeRequests()
    .antMatchers("/swagger-ui.html","/swagger-resources/**","/webjars/**", "/v2/api-docs/**").permitAll()
    .antMatchers("/api/v1/**").hasAnyAuthority("ROLE_TRUSTED_CLIENT", "ROLE_USER")
    .anyRequest().authenticated();