背景描述
最近在写一个前端端分离的微服务项目,使用到了网关zuul,然后网关的权限控制是通过springsecurity来实现的,真是踩了很多坑。
问题描述
项目配置
- 因为要进行登录认证,就放行了一部分url无需认证权限控制。
- 然后其他的所有url都需要进行认证权限控制。
- 配置代码如下:
@Configuration
public class SecurityGateway extends WebSecurityConfigurerAdapter {
// 指定要忽略的路径
private static final String[] IGNORE_PATH = {
"/maple_gateway/", "/maple_gateway/index", "/maple_gateway/login", "/maple_gateway/error.html",
"/maple_gateway/api/user_api/user_common/v1/auth/get_verify_code",
"/maple_gateway/api/user_api/user_common/v1/auth/check_verify_core"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 配置忽略url
.antMatchers(IGNORE_PATH).permitAll()
// 其他所有请求都需要认证
.anyRequest().authenticated()
}
抛出异常
- InsufficientAuthenticationException,这个异常是说权限不足,没有足够的权限访问资源。
org.springframework.security.authentication.InsufficientAuthenticationException: Full authentication is required to access this resource
- 但其实这个不是真的的异常,真正的异常是
org.springframework.security.access.AccessDeniedException: Access is denied
下面来进入到分析过程:
异常分析
由于springsecurity的异常处理和mvc的异常处理不一样,认证类异常和权限异常并不能被全局异常捕获,而是他内部自己处理的。
反向追踪链路
在发生异常时,会进入到ExceptionTranslationFilter (异常转换过滤器),将发生的异常转换为认证或者权限异常。
可以看到,在处理异常是,最初的异常是AccessDeniedException
,然后将它转换为了InsufficientAuthenticationException
异常。
然后在向上追踪,定位发生异常的过滤器。过滤器链在执行某一个过滤器方法是,发生了异常。
思路分析
现在已经明确是在执行某一个过滤器的方法是发生异常了,由于确实对springsecurity不太熟悉,导致debug的时候走了很多弯路。
- 首先,我已经将本次请求的url添加到忽略名单里了,其实这些过滤器是不会执行的。所有最开始我的debug思路错了。问题的本质是我的忽略url没有生效,而不是过滤器产生异常。
- 所有正确的做法应该是先去定位
过滤器忽略指定url的逻辑
在那里,然后去看是什么问题。 - 还要说明的一点是,正常来说,指定了正确忽略的url,内置的一些过滤器不会走,但是你自定义的,实现了
OncePerRequestFilter
的过滤器还是会走的,所有需要自己去过滤。
好了,下面先来说我的错误debug思路
错误Debug
- 我认为是发生异常的原因是我的权限问题,所有我定位到了发生异常的起点,如下图所属:
只要当访问策略投票机制返回-1,则会抛出异常。继续往下
‘’
‘’
‘’
到达三元表达式,这里也就是前面的表达式返回false就会抛出异常。继续:
‘’
‘’
‘’
这里最关键的就是框中的代码,其实这里已经将结果求出来了,为false,会抛出异常,下面那个只是去做类型转换的,现在我们要搞清楚的是为什么会出现false。继续,:
‘’
‘’
‘’
中间稍微省去了一部分,但是这里是关键,这个方法名已经很见名知意了,读取属性值,现在的属性是authenticated,就是读取他的属性,意思是否已经认证,返回true表示已经认证,也就是上面方面的返回值也就是true,也就不会抛出异常了;但是现在显然不是true,我们继续来看这个属性是怎么拿到的
‘’
‘’
‘’
可以看到,这里已经求出来是否认证了,false,未认证;这里其实就是用反射去调用了一个方法,isAuthenticated
,是否通过身份验证,继续,为什么会返回false。
‘’
‘’
‘’
结果很清晰,是否认证过的依据是是否是匿名访问,我这里是忽略url,就是要在登录之前访问,所以肯定就是匿名的访问,所以返回了true,是否认证返回了false,然后投票返回了-1,然后跑出来异常。
正确Debug
- 之所以说上面的分析是错误的,是因为这里并没有忽略url的逻辑判断,并不是我想要的东西,所以其实问题不是出在这里,而是在更前面。
- 不知道有没有有没有人注意到上面的分析过程,其实就是取属性
authenticated
的值,表示是不是已经认证过了,那这不是很奇怪吗,我配置了过滤指定url,还是走了过滤器,需要判断认证。 - 其实根本不需要,一开始去获取
authenticated
属性的值就是错的。
重新查看投票方法org.springframework.security.access.vote.AffirmativeBased.decide
可以看到,configAttributes
这里list里面存放的就是我们要求的属性值,仔细去看,有一个key是authenticated
,也就是我们要求查用户是否认证,但是不对啊,我请求的url配置了忽略认证的,怎么他的匹配模式是anyRrequest,这明显和我的配置不对啊。现在可以知道,是这个配置出了问题,我们去看这个list是怎么来的:
‘’
‘’
‘’
向上一层,可以发现是这里获取到的配置,继续:
‘’
‘’
‘’
现在是不是很清晰了,这个map是不是很熟悉,这就是我们开始配置的过滤url,然后这里就是当前请求嫩能不能和配置的忽略请求匹配的上,如果能配置上,就获取他对应的value。但是很奇怪的时,我当前请求居然和配置的对不上。
不能说完全一样吧,也就是基本相同,这居然匹配不上,去看看他的matches方法怎么实现的。
‘’
‘’
‘’
水落石出了,我靠,他里面居然是用的getRequestPath,获取到的uri是没有context前缀的,因为我这个项目是微服务的,所有我加了网关加了前缀,结果没有匹配上。文章来源:https://www.toymoban.com/news/detail-433490.html
解决
- 配置的忽略url去掉网关前缀。
现在可以看到,获取到的属性就正确了,不会在去验证权限。也就不会抛出权限不足的异常。
然后在说明一点,自己定义的过滤器还是会走,比如我这里的AuthFilter,当前的url也是忽略url,但是也还是走了过滤器,也能是我的方法不对,知道的可以告诉我。
文章来源地址https://www.toymoban.com/news/detail-433490.html
总结
- springsecurity认证忽略的url是不能包含context前缀的,否则匹配不上。
- 自定义的放行策略逻辑在
org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource#getAttributes
. - 实现
OncePerRequestFilter
的自定义过滤器不会忽略配置的url,还是会执行过滤器。 - 针对于【3】的解决方案:SpringSecurity 配置permitAll之后仍然会走自定义过滤器Filter的问题
到了这里,关于springsecurity过滤指定url【.antMatchers(***).permitAll()】失效分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!