首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用@PreAuthorize时,Spring Security返回404,而不是403

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

Stack Overflow用户
提问于 2019-04-29 22:48:48
回答 3查看 3.2K关注 0票数 4

在与此斗争了几天之后(搜索类似的问题,做试验和错误),我想放弃了……

所以问题是我有一个基于Spring Boot的REST服务,使用Spring Security和JWT进行身份验证。现在,我想保护一些方法,使其仅由使用@PreAuthorize-annotation的授权人员调用。这似乎是可行的,部分原因是Spring没有调用该方法,而是返回404。我还以为是403呢。

我已经阅读了这个SO-question,并尝试了那里给出的答案,但它没有帮助。我已经按照其他地方的建议将@EnableGlobalMethodSecurity(prePostEnabled = true)-Annotation从我的SecurityConfiguration类移动到了应用程序类,但它仍然不起作用。

我的安全配置如下所示:

代码语言:javascript
复制
@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();
}

}

控制器方法如下所示

代码语言:javascript
复制
@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);
}

我很确定我错过了一些非常简单的东西,但我就是弄不明白是什么。

顺便说一句:如果请求许可证的用户具有ADMIN角色,则一切工作正常,因此问题不是真正的404。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-04-30 17:42:38

您需要在安全配置中定义exceptionHandling,如下所示。

代码语言:javascript
复制
@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));
}

您可以按如下方式定义AccessDeniedExceptionHandler类:

代码语言:javascript
复制
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException ex) throws IOException, ServletException {
        response.setStatus(HttpStatus.FORBIDDEN);
    }
}
票数 4
EN

Stack Overflow用户

发布于 2019-04-30 16:49:59

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

代码语言:javascript
复制
@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public ResponseEntity<String> handleControllerException(Throwable ex) {
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        if(ex instanceof AccessDeniedException) {
            status = HttpStatus.FORBIDDEN;
        }

        return new ResponseEntity<>(ex.getMessage(), status);
    }
}
票数 2
EN

Stack Overflow用户

发布于 2020-08-23 23:52:36

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

要处理这个问题,可以使用SecurityConfiguration类中的注释@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)

还在another answer中提供了详细信息。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55905824

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档