使用SpringMvc和SpringSecurity时,把一个不存在的URL的设置成公开访问,并在未登录的状态下访问了,结果会返回404吗?
答案并不会,这会触发两种错误处理机制。
当URL不存在时,会触发SpringMvc的错误处理机制,将请求重定向给错误页,页面显示404
当未登录时,会触发SpringSecurity的权限校验机制,没登录会触发AccessDeniedException 异常,从而转到登录页。
这两个机制下,最终SpringSecurity会胜利,所以看到的是没权限访问。这时就会让人很懵逼,明明把URL设置了公开访问,怎么还会被拒绝?正常请求时不太会遇到这个问题,一般是在开发阶段,拼错或修改了URL,造成URL不存在。
原因分析
SpringMvc的错误处理机制,当URL不存在时,会给response打上error标签。然后会触发tomcat的错误处理机制,默认是重定向到/error错误页面。/error是由BasicErrorController 处理,MediaType是ext/html的会返回错误页面,其他情况返回ResponseEntity对象。
所以当请求重定向到/error时,会被Spring Security拦截并校验权限,未登录的情况就被拒绝了。
解决办法
方法一
权限主要是防范客户端发来的请求,对于内部转发类型的请求类型可以默认信任,所以只要调整Security Filter支持的DispatherType,只拦截request和async类型。application.yml的配置:
spring:
security:
filter:
dispatcher-types: request,async
方法二
放开对/error页面的权限校验,就是要注意错误页面的地址是可以修改的,默认获取的方法是${server.error.path:${error.path:/error}}
@Value("${server.error.path:${error.path:/error}}")
public String errorPagePath;
http.authorizeHttpRequests(it -> it
.requestMatchers(new AntPathRequestMatcher(errorPagePath)).permitAll()
其他方法
另外一个思路是在SpringMvc处理请求时,发现请求地址不存在的时候,选择抛出异常,而不是在Response里返回404状态,进而触发Tomcat的错误处理机制,这里就不再展开。