spring security 原理讲解(二)

一、spring security的过滤器

spring security 能够作为安全框架,实际上其权限拦截等功能都是基于filter实现的,下面我们讲讲security的部分过滤器

  1. SecurityContextPersistenceFilter:第一个起作用的认证过滤器。用途一:执行其他过滤器前判断用户的session(HttpSession)是否存在一个SecurityContext的上下文,如果存在则拿出来并放到SecurityContextHolder中供其他组件使用,不存在则创建SecurityContext并放入。用途二:所有过滤器执行完毕后清空SecurityContextHolder中的内容,因为SecurityContextHolder是基于ThreadLocal的,操作结束没有清空ThreadLocal会受线程机制的影响。
  2.  LogoutFilter:处理注销请求,用户发起注销时销毁用户session、清空SecurityContextHolder,并重定向到注销成功页面。
  3.  AbstractAuthenticationProcessingFilter:处理form登录的过滤器,与form有关所有操作都在此进行。
  4.  DefaultLoginPageGeneratingFilter:用来生成默认的登录页面。
  5. BasicAuthentiactionFilter、UsernamePasswordAuthenticationFilter:主要用于做basic、登录验证等身份验证处理。
  6. SecurityContextHolderAwareRequestFilter:用来包装客户请求,可以在原来请求基础上为后续提供数据
  7. RememberMeAuthenticationFilter:依赖cookie实现用户可选择的remember(记住我)功能
  8. AnonymousAuthenticationFilter:实现对匿名身份处理
  9. ExceptionTranslationFilter:获取spring security 抛出的异常信息
  10. SessionManagementFilter:防止会话伪造攻击

上面所有filter通过FilterChainProxy 结合到spring security,FilterChainProxy 会按照顺序调用filter来验证各种功能。

说明:ThreadLocal存放的东西是线程内共享的,线程间互斥的。主要为了线程内通信不用进行参数传递,从而避免一些线程并发问题。ThreadLocal内部使用了自定义的ThreadLocalMap用来存放ThreadLocal信息,该ThreadLocalMap的key为ThreadLocal对象(Thread.currentThread()),value为要共享的数据。

二、spring security 使用过滤器

上面我们只是简单提到 spring security 使用 FilterChainProxy  来执行所有定义的 Filter,下面具体讲:

1、spring security的入口filter为springSecurityFilterChain。在使用web.xml 配置的项目中,使用spring security的话需要添加如下配置:

   <filter>
       <filter-name>springSecurityFilterChain</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   </filter>

   <filter-mapping>
       <filter-name>springSecurityFilterChain</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

通过配置可以知道,我们使用到了一个DelegatingFilterProxy(一个继承GenericFilterBean的Filter)类,该类没有实际的过滤逻辑,只去寻找<filter-name>节点匹配的springSecurityFilterChain,并将过滤工作委托给springSecurityFilterChain来处理。

在springboot项目中,还记得我们之前在 WebSecurityConfigurerAdapter 的实现类 MySecurityConfig 上添加了一个 @EnableWebSecurity 注解吗。该注解会加载 WebSecurityConfiguration 类(可以从源码查找),而 WebSecurityConfiguration 类中有一个方法就是创建该 springSecurityFilterChain(Filter)。

2、springSecurityFilterChain:其实就是 FilterChainProxy(org.springframework.security.web.FilterChainProxy),即DelegatingFilterProxy 最终会把过滤工作委托给 FilterChainProxy,该类定义如下:

public class FilterChainProxy extends GenericFilterBean {
   
   private List<SecurityFilterChain> filterChains;// 

   public FilterChainProxy(SecurityFilterChain chain) {
      this(Arrays.asList(chain));
   }

   public FilterChainProxy(List<SecurityFilterChain> filterChains) {
      this.filterChains = filterChains;
   }

   public void doFilter(ServletRequest request, ServletResponse response,
         FilterChain chain) throws IOException, ServletException {
         doFilterInternal(request, response, chain);
   }

   private void doFilterInternal(ServletRequest request, ServletResponse response,
         FilterChain chain) throws IOException, ServletException {

      FirewalledRequest fwRequest = firewall
            .getFirewalledRequest((HttpServletRequest) request);
      HttpServletResponse fwResponse = firewall
            .getFirewalledResponse((HttpServletResponse) response);
		
      List<Filter> filters = getFilters(fwRequest);

      if (filters == null || filters.size() == 0) {
         fwRequest.reset();
         chain.doFilter(fwRequest, fwResponse);
         return;
      }

      VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
      vfc.doFilter(fwRequest, fwResponse);
   }

   private List<Filter> getFilters(HttpServletRequest request) {
      for (SecurityFilterChain chain : filterChains) {
         if (chain.matches(request)) {
            return chain.getFilters();
         }
      }
      return null;
   }

}

该类中很多地方有对 SecurityFilterChain 的操作,其实 SecurityFilterChain 就是所有 spring security 定义的 Filter 存放的位置。具体SecurityFilterChain 的产生可以查看 springSecurityFilterChain 产生的方法。

三、spring security的6个核心组件

1、SecurityContext:安全上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中,实际上其主要作用就是获取Authentication对象。SecurityContext的接口定义如下:

public interface SecurityContext extends Serializable {
	Authentication getAuthentication();
	void setAuthentication(Authentication authentication);
}

2、SecurityContextHolder:主要用于存放 SecurityContext 实例,所有请求会经过过滤器 SecurityContextPersistenceFilter,该过滤器会从请求的httpsession中获取安全上下文SecurityContext并放入SecurityContextHolder,在请求结束后会清除SecurityContextHolder。SecurityContextHolder可以指定SecurityContext的存储策略:

  • MODE_THREADLOCAL:将SecurityContext 存储在当前线程中,默认使用的策略。
  • MODE_INHERITABLETHREADLOCAL:将SecurityContext 存储在线程中,但子线程可以获取到父线程中的 SecurityContext。
  • MODE_GLOBAL:SecurityContext 在所有线程中都相同。

spring security存储当前认证信息的方法为:

SecurityContextHolder.getContext().setAuthentication(token); 

3、Authentication:身份认证信息,含有用户信息,接口定义如下:

public interface Authentication extends Principal, Serializable {
        //获取到的是用户的角色信息 
	Collection<? extends GrantedAuthority> getAuthorities();
        //获取证明用户认证的信息,一般为密码等信息。
	Object getCredentials();
        // 用户的其他信息
	Object getDetails();
        //获取用户身份信息,未认证时获取的是用户名,已认证时获取的是 UserDetails 
	Object getPrincipal();
        //获取当前 Authentication 是否已认证。
	boolean isAuthenticated();
        //设置当前 Authentication 是否已认证
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

4、AuthenticationManager:只有一个接收Authentication参数的authenticate方法,主要用于校验Authentication,如果失败会抛出AuthenticationException实现类(BadCredentialsException、DisabledException等)对应的异常。,其接口定义如下:

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
			throws AuthenticationException;
}

5、Userdetails :存储用户信息,其接口定义如下:

public interface UserDetails extends Serializable {
        //获取用户的角色信息。
	Collection<? extends GrantedAuthority> getAuthorities();
        //获取密码
	String getPassword();
        //获取用户名
	String getUsername();
        //账户是否过期
	boolean isAccountNonExpired();
        //账户是否被锁定
	boolean isAccountNonLocked();
        //证书是否过期
	boolean isCredentialsNonExpired();
        //是否可用
	boolean isEnabled();
}

6、UserDetailsService:该接口只有一个 loadUserByUsername(String name) 方法用来获取UserDetails。用户数据保存在数据库的时候我们就需要实现该类,从数据库获取用户的身份以及角色等信息,并设置到UserDetails的实现类(org.springframework.security.core.userdetails.User),再返回。loadUserByUsername通过数据库获取用户信息的时候,若用户不存在一定要抛出(org.springframework.security.core.userdetails.UsernameNotFoundException)异常。

四、其他知识点:

1、权限缓存:spring security通过CachingUserDetailsService来进行缓存,其实现UserDetailsService接口,spring security加载UserDetails时先从缓存获取,没有才去持久的UserDetailsService实现类获取,再将UserDetails放到缓存。

2、security 自定义决策管理器:AbstractAccessDecisionManager,其核心方法是supports方法,该方法含有对AccessDecisionVoter投票器的操作,有无权限访问最终就是由投票器决定的(投票器 RoleVoter.java的vote方法)

3、security的优缺点:

  • 优点:提供一套安全框架、提供很多用户认证功能减少开发量、易于继承到spring
  • 缺点:RBAC(Role-Based access Control)不明显、大数据量下不可用、没有可操作的管理界面
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值