Después de luchar con esto durante unos días (SO busca de preguntas similares, haciendo prueba y error), me siento tentado a renunciar a ...
Así que el problema es que tengo un servicio REST basada en la primavera de arranque utilizando Spring Seguridad y JWT para la autenticación. Ahora quiero conseguir algunos de los métodos que sólo pueden ser llamados por personas autorizadas utilizando el @PreAuthorize
-annotation. Esto parece funcionar, en parte porque en lugar de llamar al método primavera vuelve 404. Yo habría esperado 403.
He leído este SO-pregunta y trató de las respuestas dadas allí, pero no sirvió de nada. He movido el @EnableGlobalMethodSecurity(prePostEnabled = true)
-Annotation de mi SecurityConfiguration a la clase de aplicaciones como se sugiere en otro lugar, todavía no funciona.
Mi configuración de seguridad es el siguiente:
@Configuration
@Profile("production")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Value("${adDomain}")
private String adDomain;
@Value("${adUrl}")
private String adUrl;
@Value("${rootDn}")
private String rootDn;
@Value("${searchFilter}")
private String searchFilter;
private final AuthenticationManagerBuilder auth;
private final SessionRepository sessionRepository;
@Autowired
public SecurityConfiguration(AuthenticationManagerBuilder auth, SessionRepository sessionRepository) {
this.auth = auth;
this.sessionRepository = sessionRepository;
}
@Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/static/**", "/api/web/logout")
.antMatchers(HttpMethod.POST, "/api/web/login");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // Using JWT there is no need for CSRF-protection!
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
ActiveDirectoryLdapAuthenticationProvider adProvider =
new ActiveDirectoryLdapAuthenticationProvider(adDomain, adUrl, rootDn);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
adProvider.setSearchFilter(searchFilter);
adProvider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
auth.authenticationProvider(adProvider);
return super.authenticationManagerBean();
}
}
El aspecto de método controlador como este
@RequestMapping(path = "/licenses", method = RequestMethod.GET)
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> getAllLicenses(@RequestParam("after") int pagenumber, @RequestParam("size") int pagesize
, @RequestParam("searchText") String searchText) {
List<LicenseDTO> result = ...
return new ResponseEntity<Object>(result, HttpStatus.OK);
}
Estoy muy seguro de que me falta algo muy simple, pero es que no puedo entender por qué.
Por cierto: si el usuario que solicita la licencia tiene la función de Administrador, todo funciona como se esperaba, por lo que el problema no es un verdadero 404.
Es necesario definir el ExceptionHandling en la configuración de seguridad de la siguiente manera,
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // Using JWT there is no need for CSRF-protection!
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new AccessDeniedExceptionHandler())
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}
Se puede definir la clase AccessDeniedExceptionHandler de la siguiente manera,
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException ex) throws IOException, ServletException {
response.setStatus(HttpStatus.FORBIDDEN);
}
}