Java 使用@PreAuthorize时,Spring Security返回404而不是403

Java 使用@PreAuthorize时,Spring Security返回404而不是403,java,spring-boot,spring-security,Java,Spring Boot,Spring Security,经过几天的努力(寻找类似的问题,尝试错误),我很想放弃 所以问题是我有一个基于Spring引导的REST服务,使用Spring安全性和JWT进行身份验证。现在,我想保护一些方法,这些方法只能由授权人员使用@PreAuthorize-注释调用。 这似乎部分是因为Spring没有调用方法,而是返回404。我原以为是403 我读过这篇文章,并尝试了其中给出的答案,但没有帮助。我已将@EnableGlobalMethodSecurity(prespenabled=true)-注释从我的SecurityC

经过几天的努力(寻找类似的问题,尝试错误),我很想放弃

所以问题是我有一个基于Spring引导的REST服务,使用Spring安全性和JWT进行身份验证。现在,我想保护一些方法,这些方法只能由授权人员使用
@PreAuthorize
-注释调用。 这似乎部分是因为Spring没有调用方法,而是返回404。我原以为是403

我读过这篇文章,并尝试了其中给出的答案,但没有帮助。我已将
@EnableGlobalMethodSecurity(prespenabled=true)
-注释从我的SecurityConfiguration移动到应用程序类,如其他地方所建议的,但它仍然不起作用

我的安全配置如下所示:

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

控制器方法如下所示

@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);
}
@RequestMapping(path=“/licenses”,method=RequestMethod.GET)
@预授权(“hasRole('ADMIN')”)
公共响应getAllLicenses(@RequestParam(“after”)int pagenumber,@RequestParam(“size”)int pagesize
,@RequestParam(“searchText”)字符串searchText){
列表结果=。。。
返回新的响应状态(结果,HttpStatus.OK);
}
我很确定我遗漏了一些非常简单的东西,但我就是不知道是什么


顺便说一句:如果请求许可证的用户具有管理员角色,那么一切都按预期进行,因此问题不是真正的404。

我终于找到了一个适合我的目的的解决方案。我不知道这是否是处理此问题的最佳方法,但添加ExceptionHandler就成功了。在过滤器链的深处,当没有这样的处理程序时,403变异为404。 也许我是为了阅读和理解文档,但我没有发现任何建议您必须这样做的内容。因此,也许我解决这样的问题是错误的,但下面是实现这一技巧的代码(这是一个非常基本的实现,应该随着时间的推移而改进):

@ControllerAdvice
公共类MyExceptionHandler{
@ExceptionHandler(Throwable.class)
@应答器
公共响应手动控制器异常(可丢弃的ex){
HttpStatus status=HttpStatus.INTERNAL\u SERVER\u错误;
if(例如AccessDeniedException){
status=HttpStatus.probled;
}
返回新的ResponseEntity(例如getMessage(),status);
}
}

您需要在安全配置中定义异常处理,如下所示:

@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));
}
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException ex) throws IOException, ServletException {
        response.setStatus(HttpStatus.FORBIDDEN);
    }
}
您可以如下定义AccessDeniedExceptionHandler类

@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));
}
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException ex) throws IOException, ServletException {
        response.setStatus(HttpStatus.FORBIDDEN);
    }
}

可以借助注释
@EnableGlobalMethodSecurity(preprestenabled=true)
启用全局方法安全性。这与
@Preauthorize
的组合将为您的控制器创建一个新的代理,它将丢失请求映射,这将导致404异常

要处理此问题,可以使用
@EnableGlobalMethodSecurity(preprestenabled=true,proxyTargetClass=true)
注释,该注释位于
安全配置
类中


在中也提供了详细信息。

确切的Spring安全响应是什么?响应正文中没有任何内容。只是标题中的404状态。在进行更多调试时,我发现最初有403。它只是在过滤器链的更上层更改为404。您可以将
Throwable.class
更改为
AccessDeniedException.class
,而不必使用
instanceof