SpringSecurity过滤器原理
共 4810字,需浏览 10分钟
·
2021-12-10 12:20
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
- 主要过滤器链 -
SpringSecurity的功能主要是由一系列的过滤器链相互配合完成的。验证一个过滤器之后放行到下一个过滤器链,然后到最后。
- 认证流程 -
- 过滤器作用 -
SecurityContextPersistenceFilter:会在每次请求处理之前从配置好的SecurityContextRepository中获取SecurityContext安全上下文信息,然后加载到SecurityContextHolder中,然后在该次请求处理完成之后,将SecurityContextHolder中关于这次请求的信息存储到一个“仓库”中,然后将SecurityContextHolder中的信息清除,例如在Session中维护一个用户的安全信息就是这个过滤器处理的。
DefaultLoginPageGeneratingFilter:如果没有配置自定义登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
BasicAuthenticationFilter:检测和处理http basic认证。
UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自/login的表单action。从表单中获取用户名和密码时,默认使用的表单name属性值为username和password,这俩个值也可以通过usernameParameter和passwordParameter在配置中自定义。
这个过滤器在表单提交登录请求之时会起作用。那么假设现在采用SpringSecurity整合Jwt,那么我需要配置一个Jwt登录认证类(继承BasicAuthenticationFilter或者继承OncePerRequestFilter都可以,因为BasicAuthenticationFilter继承了OncePerRequestFilter),重写过滤器方法。Jwt的token认证登录是需要在在采用用户名密码登录认证之前,所以在配置Jwt登录认证类的时候需要在UsernamePasswordAuthenticationFilter之前添加过滤器。
//配置自定义过滤器 添加jwt登录授权过滤器
//在过滤器UsernamePasswordAuthenticationFilter之前
http.addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);RequestCacheAwareFilter:用来处理请求的缓存。
SecurityContextHolderAwareRequestFilter:主要是包装请求对象request。
AnonymousAuthenticationFilter:检测SecurityContextHolder中是否存在Authentication对象,如果不存在则为其提供一个匿名Authentication。
SessionManagementFilter:管理Session的过滤器
ExceptionTranslationFilter:捕获来自过滤器链的所有异常,并进行处理。但是只处理两类异常:AccessDeniedException和AuthenticationException 异常,其他的异常会继续抛出。
如果捕获到的AuthenticationException,那么将会使用其对应的AuthenticationEntryPoint的commence()方法处理。在处理之前,ExceptionTranslationFilter先使用RequestCache将当前的HTTPServletRequest的信息保存起来,方便用户登录成功后可以跳转到之前的页面。
可以自定义AuthenticationException的处理方法。需要实现AuthenticationEntryPoint接口,然后重写commence()方法。
/**
* 当未登录或者token失效时访问接口自定义的返回结果
*/
@Component
public class RestfulAuthorizationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
RespBean bean = RespBean.error("请先登录!");
bean.setCode(401);
writer.write(new ObjectMapper().writeValueAsString(bean));
writer.flush();
writer.close();
}
}如果捕获的AuthenticationDeniedException,那么将会根据当前访问的用户是否已经登录认证做不同的处理,如果未登录,则会使用关联的AuthenticationEntryPoint的commence()方法进行处理,否则将使用关联的AccessDeniedHandler的handle()方法进行处理。
可以进行自定义AuthenticationDeniedException的处理方法。需要实现AccessDeniedHandler接口,然后重写handle()方法。
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
RespBean error = RespBean.error("权限不足,联系管理员!");
writer.write(new ObjectMapper().writeValueAsString(error));
error.setCode(403);
writer.flush();
writer.close();
}
}FilterSecurityInterceptor:可以看做过滤器链的出口
RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
- SecurityContextHolder -
SecurityContext对象是安全上下文信息,包括当前使用系统的用户的信息。每个用户都会有它的安全上下文对象,所以把每一个用户的SecurityContext保存到SecurityContextHolder中。
SecurityContextHolder存储SecurityContext的方式根据应用场景不同也有区别:
(1)单机系统,即应用从开启到关闭的整个生命周期只有一个用户在使用。由于整个应用只需要保存一个SecurityContext(安全上下文即可)
(2)多用户系统,比如典型的Web系统,整个生命周期可能同时有多个用户在使用。这时候应用需要保存多个SecurityContext(安全上下文),需要利用ThreadLocal进行保存,每个线程都可以利用ThreadLocal获取其自己的SecurityContext,及安全上下文。ThreadLocal内部会用数组来存储多个对象的。原理是,ThreadLocal会为每个线程开辟一个存储区域,来存储相应的对象。
Authentication:用户信息的表示
在SecurityContextHolder中存储了当前与系统交互的用户的信息。Spring Security使用一个Authentication 对象来表示这些信息。
Authentication 主要包含了:
用户权限集合
用户证书(密码)
细节(Details)
Principal(就是这个用户的账户信息)
在自定义登录认证过滤器的时候,记得需要把用户的信息(Authentication )保存到SecurityContextHolder中,以便后续用户的正常使用。比如我在做和Jwt认证的整合的时候,继承OncePerRequestFilter,重写doFilterInternal方法,认证完token之后,就需要把用户的信息存入安全上下文Holder中。
UsernamePasswordAuthenticationToken authenticationToken
=new UsernamePasswordAuthenticationToken(user,null, null);
authenticationToken.setDetails(new WebAuthenticationDetailsSource()
.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
作者 | ins1mnia
来源 | cnblogs.com/ins1mnia/p/15635130.html
加锋哥微信: java1239 围观锋哥朋友圈,每天推送Java干货!