Java 使用@PreAuthorize时,Spring Security返回404而不是403
经过几天的努力(寻找类似的问题,尝试错误),我很想放弃 所以问题是我有一个基于Spring引导的REST服务,使用Spring安全性和JWT进行身份验证。现在,我想保护一些方法,这些方法只能由授权人员使用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
@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
。