Spring Boot中通过@EnableGlobalMethodSecurity进行端点保护配置

时间:2017-10-20 02:11:42

标签: spring rest spring-boot spring-security spring-security-oauth2

在尝试重新演示如何限制访问端点的书籍中的示例时,我遇到了意外行为 - 不允许具有许可角色的管理员访问:

    $ curl -X POST \
>   'http://localhost:9090/oauth/token?grant_type=password&username=admin&password=password2' \
>   -H 'authorization: Basic d2ViYXBwOndlYnNlY3JldA==' \
>   -H 'cache-control: no-cache' \
>   -d '"category":"test","document":"this is a test document"'
    {"access_token":"6d149c21-6a48-41e8-885d-d6da70648b49","token_type":"bearer","expires_in":42860,"scope":"read,write,trust"}

$ curl -X GET \
>   'http://localhost:9090/resource?access_token=6d149c21-6a48-41e8-885d-d6da70648b49' \
>   -H 'cache-control: no-cache' \

{"timestamp":1508464945487,"status":403,"error":"Forbidden","exception":"org.springframework.security.access.AccessDeniedException","message":"Access Denied","path":"/resource"} 

以下是相关课程:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user1").password("password1").roles("USER")
        .and().withUser("admin").password("password2").roles("ADMIN");
    }

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().antMatchers("/**").permitAll().and()
        // default protection for all resources (including /oauth/authorize)
            .authorizeRequests()
                .anyRequest().hasAnyRole("USER","ADMIN");
        // ... more configuration, e.g. for form login
   }

}
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authManager;

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("webapp").secret("websecret").authorizedGrantTypes("password")
                .scopes("read,write,trust");
    }



}
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class OauthServerApplication {


    @RequestMapping("/resource")
    @PreAuthorize("hasRole('ADMIN')")
    public String resourceEndpoint() {
        return "This resource is protected by the resource server.";
    }

    public static void main(String[] args) {
        SpringApplication.run(OauthServerApplication.class, args);
    }
}

我在这里缺少什么?

目标是只允许admin用户访问资源端点,而拒绝访问常规用户user1

N.B。:下面的节实际上是我添加的内容:

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().antMatchers("/**").permitAll().and()
        // default protection for all resources (including /oauth/authorize)
            .authorizeRequests()
                .anyRequest().hasAnyRole("USER","ADMIN");
        // ... more configuration, e.g. for form login
   }

因为缺席在资源获取尝试期间(第二次请求)产生以下内容:

<html>
    <head>
        <title>Login Page</title>
    </head>
    <body onload='document.f.username.focus();'>
        <h3>Login with Username and Password</h3>
        <form name='f' action='/login' method='POST'>
            <table>
                <tr>
                    <td>User:</td>
                    <td>
                        <input type='text' name='username' value=''>
                    </td>
                </tr>
                <tr>
                    <td>Password:</td>
                    <td>
                        <input type='password' name='password'/>
                    </td>
                </tr>
                <tr>
                    <td colspan='2'>
                        <input name="submit" type="submit" value="Login"/>
                    </td>
                </tr>
                <input name="_csrf" type="hidden" value="1cbdad0b-181e-496c-aed0-eb633b29eab7" />
            </table>
        </form>
    </body>
</html>

启用网络安全日志后,正如一些评论员所建议的那样:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

.....

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.debug(true);
    } 
...}

这里是来自2个请求的部分结果服务器日志:

2017-10-20 07:17:27.836  WARN 12728 --- [           main] o.s.s.c.a.web.builders.WebSecurity       : 

********************************************************************
**********        Security debugging is enabled.       *************
**********    This may include sensitive information.  *************
**********      Do not use in a production system!     *************
********************************************************************


2017-10-20 07:17:29.116  INFO 12728 --- [           main] o.s.cloud.commons.util.InetUtils         : Cannot determine local hostname
2017-10-20 07:17:29.180  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-10-20 07:17:29.186  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'configurationPropertiesRebinder' has been autodetected for JMX exposure
2017-10-20 07:17:29.187  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'refreshEndpoint' has been autodetected for JMX exposure
2017-10-20 07:17:29.187  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'restartEndpoint' has been autodetected for JMX exposure
2017-10-20 07:17:29.187  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'environmentManager' has been autodetected for JMX exposure
2017-10-20 07:17:29.188  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'refreshScope' has been autodetected for JMX exposure
2017-10-20 07:17:29.189  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'environmentManager': registering with JMX server as MBean [org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager]
2017-10-20 07:17:29.197  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'restartEndpoint': registering with JMX server as MBean [org.springframework.cloud.context.restart:name=restartEndpoint,type=RestartEndpoint]
2017-10-20 07:17:29.202  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'refreshScope': registering with JMX server as MBean [org.springframework.cloud.context.scope.refresh:name=refreshScope,type=RefreshScope]
2017-10-20 07:17:29.207  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'configurationPropertiesRebinder': registering with JMX server as MBean [org.springframework.cloud.context.properties:name=configurationPropertiesRebinder,context=6f7923a5,type=ConfigurationPropertiesRebinder]
2017-10-20 07:17:29.211  INFO 12728 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'refreshEndpoint': registering with JMX server as MBean [org.springframework.cloud.endpoint:name=refreshEndpoint,type=RefreshEndpoint]
2017-10-20 07:17:29.338  INFO 12728 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2017-10-20 07:17:29.402  INFO 12728 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 9090 (http)
2017-10-20 07:17:29.406  INFO 12728 --- [           main] c.e.spring.cloud.OauthServerApplication  : Started OauthServerApplication in 6.879 seconds (JVM running for 7.264)
2017-10-20 07:18:20.584  INFO 12728 --- [nio-9090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-10-20 07:18:20.584  INFO 12728 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-10-20 07:18:20.600  INFO 12728 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 16 ms
2017-10-20 07:18:20.606  INFO 12728 --- [nio-9090-exec-1] Spring Security Debugger                 : 

************************************************************

Request received for POST '/oauth/token?grant_type=password&username=user1&password=password1':

org.apache.catalina.connector.RequestFacade@5ace935e

servletPath:/oauth/token
pathInfo:null
headers: 
host: localhost:9090
connection: keep-alive
content-length: 54
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
cache-control: no-cache
origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
content-type: text/plain;charset=UTF-8
authorization: Basic d2ViYXBwOndlYnNlY3JldA==
postman-token: cd55952b-6c6f-6101-8f24-3942dee9b06a
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.8
cookie: JSESSIONID=F4526A8B6FD15FD35D3D84D25E2C3898


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************


2017-10-20 07:20:18.604  INFO 12728 --- [nio-9090-exec-4] Spring Security Debugger                 : 

************************************************************

Request received for GET '/resource?access_token=5466dfff-f088-4097-8db9-4ed07f0b80a0':

org.apache.catalina.connector.RequestFacade@5ace935e

servletPath:/resource
pathInfo:null
headers: 
host: localhost:9090
connection: keep-alive
cache-control: no-cache
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
postman-token: 394b6610-1d18-eec7-338b-ab1de65cfeeb
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.8
cookie: JSESSIONID=F4526A8B6FD15FD35D3D84D25E2C3898


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  CsrfFilter
  LogoutFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************


2017-10-20 07:20:18.640  INFO 12728 --- [nio-9090-exec-4] Spring Security Debugger                 : 

************************************************************

New HTTP session created: 1D899B6306F970E097CE746030A28E4A

Call stack: 

    at org.springframework.security.web.debug.Logger.info(Logger.java:44)
    at org.springframework.security.web.debug.DebugRequestWrapper.getSession(DebugFilter.java:166)
    at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
    at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
    at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
    at org.springframework.security.web.savedrequest.HttpSessionRequestCache.saveRequest(HttpSessionRequestCache.java:59)
    at org.springframework.security.web.access.ExceptionTranslationFilter.sendStartAuthentication(ExceptionTranslationFilter.java:201)
    at org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:177)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:133)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:90)
    at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:77)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)


************************************************************

当我在资源端点顶部注释掉@PreAuthorize("hasRole('ADMIN')")注释时,资源请求会正确显示&#34;此资​​源受资源服务器保护。&#34;还会触发放置在那里的断点。重新启用@PreAuthorize注释似乎会引入不正确的行为并跳过断点。我可以在周围的Spring基础设施中放置断点来深入研究@PreAuthorize("hasRole('ADMIN')")注释的这种影响吗?

感谢。

1 个答案:

答案 0 :(得分:0)

由于您使用的是 OAuth2 ,因此您需要使用 Bundle extras = getIntent().getExtras(); String transportItemChosen = extras.getString("SpinnerValue"); transportItemChosen = transportItemChosen.replace(".png", ""); transportItemChosen = transportItemChosen.replace("res/drawable/", ""); String uri = ("@drawable/" + transportItemChosen); int id = getResources().getIdentifier(uri, null, getPackageName()); ImageView mImageView; mImageView = (ImageView) findViewById(R.id.selectedimage); mImageView.setImageResource(id); 而不是WebSecurityConfigurerAdapter。例如:

ResourceServerConfigurerAdapter

现在您应该可以使用安全方法了。

Here OAuth2 的实施,其中 Spring Boot 包含自定义@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().permitAll().and().cors().disable().csrf().disable().httpBasic().disable() .exceptionHandling() .authenticationEntryPoint( (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) .accessDeniedHandler( (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)); } } 服务和 JWT令牌。基本实现是一样的。

Here是授权服务器的UserDetails

Here是授权服务器的OAuth2Configuration