0

Lors de la tentative de recréation d'un exemple de livre montrant comment restreindre l'accès à un point de terminaison, j'ai un comportement inattendu - un administrateur avec un rôle de validation est interdit:Configuration de la protection des points de terminaison via @EnableGlobalMethodSecurity dans Spring Boot

$ 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"} 

Voici les classes appropriées:

@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); 
    } 
} 

Que manque-t-il ici?

L'objectif est d'avoir seulement admin utilisateur pour être en mesure d'accéder au point de terminaison de la ressource, tandis que l'utilisateur régulier user1 doit se voir refuser l'accès.

NB: la strophe ci-dessous est en fait quelque chose que j'ajouté:

@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 
    } 

que son absence produit les éléments suivants lors de tentative d'acquisition des ressources (2e demande):

<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> 

Après avoir activé web journal de sécurité, comme l'ont suggéré certains commentateurs:

@Configuration 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

..... 

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

est ici le journal du serveur résultant partielle des 2 demandes:

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': 

[email protected] 

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': 

[email protected]ace935e 

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) 


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

Lorsque je commente sur l'annotation @PreAuthorize("hasRole('ADMIN')") au-dessus du point de terminaison des ressources, la demande de ressources affiche correctement « Cette ressource est protégée par le serveur de ressources. " Un point d'arrêt placé là-bas est également déclenché. La réactivation de l'annotation @PreAuthorize semble introduire le comportement incorrect et ignorer le point d'arrêt. Où dans l'infrastructure de printemps environnante pourrais-je placer le point de rupture pour approfondir cet effet de @PreAuthorize("hasRole('ADMIN')") annotation?

Merci.

+0

Est-ce que l'articulé livre sur la façon dont le point final de repos est censé savoir ce jeton représente cet utilisateur? Quand j'ai vu cela avec JWT, Spring est capable de dériver un SecurityContext depuis le JWT lui-même. Ce jeton, cependant, ne ressemble pas à un JWT (il ne semble pas être dans le bon format pour un JWT). Ce qui n'est pas clair pour moi, c'est comment le contexte de requête devrait prendre ce qui ressemble à un uuid et dériver que SecurityContext en est un avec un rôle Admin? – EdH

+0

@EdH: il ne fait que lister les étapes et montre qu'avec la configuration j'ai listé le jeton OAuth produit via '' '@ EnableAuthorizationServer''' lors de la 1ère requête, dans le 2ème permet de voir le contenu String de la ressource exposée via '' '@ EnableResourceServer''' &' '' @ RestController''' seulement pour l'utilisateur admin et non pour l'utilisateur normal. Ils ne discutent pas de JWT. Dans mon cas, il semble les interdire tous les deux. –

+0

OK, j'ai négligé de voir que le authServer et le serveur de ressources sont le même processus. Donc, peut-être qu'il peut accéder au SecurityContext en utilisant cet uuid, car il est probablement stocké en interne. – EdH

Répondre

0

Parce que vous utilisez OAuth2, au lieu d'utiliser la WebSecurityConfigurerAdapter vous devez utiliser le ResourceServerConfigurerAdapter. Par exemple:

@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)); 
    } 
} 

Vous devriez maintenant pouvoir utiliser les méthodes de sécurité.

Here est une implémentation pour OAuth2 avec Spring Boot avec un service sur mesure et UserDetailsJWT jeton. L'implémentation de base est la même.

Here est le OAuth2Configuration pour le serveur d'autorisation

Here est le WebSecurityConfiguration pour le serveur d'autorisation