Spring Security filter探究(一)

Spring Security架构

在这里插入图片描述

从官方这张图可以看出,Spring Security是通过内部的过滤器链实现认证和授权逻辑的。Spring Security内部的过滤器是有先后顺序的。比如UsernamePasswordAuthenticationFilter如果认证成功,那么AnonymousAuthenticationFilter肯定就不需要设置匿名者了。所以UsernamePasswordAuthenticationFilter在AnonymousAuthenticationFilter之前。具体顺序可看官网说明。

最后两个filter

  先说倒数第一个Filter:SecurityInterceptor。这个filter是最终做出抉择的地方,是通过还是拒绝会在filter中做出决定。如果通过则完成了Spring Security内部的过滤器链,继续执行web容器的其它过滤器。如果拒绝则会抛出异常。抛出的异常则会被前一个filter也就是ExceptionTranslationFilter拦截到。
  ExceptionTranslationFilter就是倒数第二个filter。该filter的doFilter方法只是调用过滤器链的下一个filter也就是FilterSecurityInterceptor,但是会捕获所有异常,下面是简化版的异常处理方法,当然还有其它类型异常,Spring Security直接就抛出了不会做处理。

		if (exception instanceof AuthenticationException) {
			
		}else if (exception instanceof AccessDeniedException) {
		}

可以看出就是针对认证异常和授权异常。对于这两者都有默认的处理器,当然Spring Security也提供了覆盖的方式。就是对于httpSecurity的配置时做如下的配置

               httpSecurity.exceptionHandling()
                .authenticationEntryPoint(xx)
                .accessDeniedHandler(xx)

常用的UsernamePasswordAuthenticationFilter浅析

  当配置httpSecurity.loginForm的时候,就开启了UsernamePasswordAuthenticationFilter。但是否使用还要看登录url是否为该filter处理的url。默认配置为/login。当然也可以修改,就是使用如下配置指定。

httpSecurity.formLogin().loginProcessingUrl("xxx")

该filter默认就会执行配置的UserDetailsService的loadUserByUsername方法。该filter的dofilter在其父类中,下面是dofilter最后几句代码

		catch (AuthenticationException failed) {
   		// Authentication failed
   		unsuccessfulAuthentication(request, response, failed);

   		return;
   	}
		// Authentication success
   	if (continueChainBeforeSuccessfulAuthentication) {
   		chain.doFilter(request, response);
   	}

   	successfulAuthentication(request, response, chain, authResult);

可以看到认证失败则走unsuccessfulAuthentication处理。成功时如果continueChainBeforeSuccessfulAuthentication为true,才会继续走其它过滤器,而默认值为false。所以如果使用UsernamePasswordAuthenticationFilter处理登录,则默认是不走controller的,而是通过successfulAuthentication处理登录成功的响应。
为啥要这样设置呢?以下为个人见解:

  1. 我们知道filter的dofilter方法中chain.doFilter(request, response)后的代码是要等到servlet中的逻辑处理完成之后才会触发的。而在UserDetailsService的loadUserByUsername方法中已经完成了类似用户登录的校验及响应操作,所以没必要走controller层。

  2. 如果我们使用json格式提交,自定义一个类似UsernamePasswordAuthenticationFilter的filter,这里就叫JsonUsernamePasswordAuthenticationFilter。如果要提取用户名,则需要从request的输入流中读取,类似如下代码,然后使用json解析该字符串。

String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);

因为流只能读取一次,所以如果继续让代码走到controller层定义的登录接口,那么就会报出400Bad Request错误,因为request中已无数据。

  1. 没找到如何设置UsernamePasswordAuthenticationFilter的该参数为true,也没必要。(^o^)

successfulAuthentication方法内部的默认successHandler会将context保存到session中,如果重写了其中的successHandler也没用,因为SecurityContextPersistenceFilter会在dofilter最后再次执行一次保存到session中如果之前没有保存的话。如下所示。

try{
	chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
	repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());

当其它请求过来时,SecurityContextPersistenceFilter这个过滤器会从session中取出context设置到SecurityContextHolder中。
当然如果关闭了session的话

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

SecurityContextPersistenceFilter中的repo变量类型会发生变化,所以不会存入到session中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值