Spring Security OAuth2 在 REST 服务上被拒绝访问

Posted

技术标签:

【中文标题】Spring Security OAuth2 在 REST 服务上被拒绝访问【英文标题】:Spring Security OAuth2 get Access Denied on REST Service 【发布时间】:2019-05-17 08:38:11 【问题描述】:

我正在尝试使用 Spring 安全性和 OAuth2 来保护我的 Spring REST 服务。我设法得到了令牌:

tokens

但是当我尝试获得安全的休息服务时,我的访问被拒绝:

denied

谁能告诉我为什么会这样?

授权服务器配置:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 

    private static String REALM="EXAMPLE_REALM";

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserApprovalHandler handler;

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 

        clients.inMemory()
            .withClient("myRestClient") // client id
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("read", "write", "trust")
            .secret("noopP@ssw0rd")
            .accessTokenValiditySeconds(240).//invalid after 4 minutes.
            refreshTokenValiditySeconds(600);//refresh after 10 minutes.
    

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.tokenStore(tokenStore).userApprovalHandler(handler)
                .authenticationManager(authManager);
    

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception 
        oauthServer.realm(REALM+"/client");
    


资源服务器配置:

@Configuration
@EnableResourceServer

public class ResourceServerConfig extends ResourceServerConfigurerAdapter 

    private static final String RESOURCE_ID = "SPRING_REST_API";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) 
        resources.resourceId(RESOURCE_ID).stateless(false);
    

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http.
        anonymous().disable()
        .requestMatchers().antMatchers("/api/**")
        .and().authorizeRequests()
        .antMatchers("/api/**").access("hasRole('ADMIN')")
        .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    


安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private ClientDetailsService clientService;

    @Bean
    public PasswordEncoder passwordEncoder() 
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    
    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception        
        auth
            .inMemoryAuthentication()
                .withUser("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER")
            .and()
                .withUser("admin")
                .password(passwordEncoder().encode("admin"))
                .roles("ADMIN");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
        .csrf().disable()
        .anonymous().disable()
        .authorizeRequests()
        .antMatchers("/oauth/token").permitAll()
        .antMatchers("/about").authenticated();
    

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


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

    @Bean
    @Autowired
    public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore)
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientService));
        handler.setClientDetailsService(clientService);
        return handler;
    

    @Bean
    @Autowired
    public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception 
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);
        return store;
    


AboutController(休息服务):

    @RestController
public class AboutController 
    @RequestMapping(value = "/about", method = RequestMethod.GET)
    public ResponseEntity<?> home() 
        return new ResponseEntity<>("This is a demo application to show how to secure REST API using Spring Security and OAuth2", HttpStatus.OK);
    

application.properties:

server.servlet.context-path=/api
security.oauth2.resource.filter-order = 3

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-rest-service</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

提前致谢!

【问题讨论】:

一目了然:您的内存中身份验证定义了ROLE_CLIENT,但您的端点正在寻找ROLE_ADMIN 替代方案:如果您将令牌作为承载者放在Authorization 标头中会发生什么?所以看起来像Authorization: Bearer &lt;UUID&gt;。将令牌作为查询参数传递到您的 URL 中似乎很尴尬并且可能损坏,因此理想情况下不需要存在... 【参考方案1】:

得到了这个工作。我的问题是我在 application.config 文件中添加了 /api 以便我的所有服务都有一个 api 文件夹,例如:http://localhost:8080/api/about .. 在我的 ResourceServerConfig 类中,我添加了 /api/** 作为 requestMatchers 和 antMatchers .... sercurity 配置正在此 api 前缀文件夹之后寻找 serverice .... 所以公开我的 about rest 完成这样:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter 

    private static final String RESOURCE_ID = "SPRING_REST_API";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) 
        resources.resourceId(RESOURCE_ID).stateless(false);
    

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http.
        anonymous().disable()
        .requestMatchers().antMatchers("/about/**")
        .and().authorizeRequests()
        .antMatchers("/about/**").access("hasRole('ADMIN')")
        .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    


顺便说一句..我可以使用请求屏幕截图中的参数请求 about 服务,但如果我提供授权令牌也可以作为承载令牌授权标头....

问候!

彼得

【讨论】:

以上是关于Spring Security OAuth2 在 REST 服务上被拒绝访问的主要内容,如果未能解决你的问题,请参考以下文章

oauth2 spring-security 如果您在请求令牌或代码之前登录

使用 spring-session 和 spring-cloud-security 时,OAuth2ClientContext (spring-security-oauth2) 不会保留在 Redis

Spring Security Oauth2

Spring-Security OAuth2 设置 - 无法找到 oauth2 命名空间处理程序

Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt

针对授权标头的Spring Security OAuth2 CORS问题