Spring Security学习笔记-Filter

  1. SecurityContextPersistenceFilter
       SecurityContextPersistenceFilter位于过滤器的顶端,是第一个起作用的过滤器。它的第一个用途是在执行其他过滤器之前率先判断用户的session是否已经存在了一个springSecurity上下文的securityContext,如果存在就把securityContext拿出来,放到securityContext的holder中,供springSecurity的其他部分使用;如果不存在,就创建一个securityContext出来,还是放到securityContext的holder中,供SpringSecurity的其他部分使用。它的第二个用途,是在所有过滤器执行完毕之后,清空securityContext的holder中的内容,因为securityContextHolder是基于threadLocal的,如果在操作完成后,没有清空threadLocal,会受到服务器的线程池机制的影响。
public class SecurityContextPersistenceFilter extends GenericFilterBean {

	static final String FILTER_APPLIED = "__spring_security_scpf_applied";

	private SecurityContextRepository repo;

	private boolean forceEagerSessionCreation = false;

	public SecurityContextPersistenceFilter() {
		this(new HttpSessionSecurityContextRepository());
	}

	public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
		this.repo = repo;
	}

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		if (request.getAttribute(FILTER_APPLIED) != null) {
			// ensure that filter is only applied once per request
			chain.doFilter(request, response);
			return;
		}

		final boolean debug = logger.isDebugEnabled();

		request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

		if (forceEagerSessionCreation) {
			HttpSession session = request.getSession();

			if (debug && session.isNew()) {
				logger.debug("Eagerly created session: " + session.getId());
			}
		}

		HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
				response);
		SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

		try {
			SecurityContextHolder.setContext(contextBeforeChainExecution);

			chain.doFilter(holder.getRequest(), holder.getResponse());

		}
		finally {
			SecurityContext contextAfterChainExecution = SecurityContextHolder
					.getContext();
			// Crucial removal of SecurityContextHolder contents - do this before anything
			// else.
			SecurityContextHolder.clearContext();
			repo.saveContext(contextAfterChainExecution, holder.getRequest(),
					holder.getResponse());
			request.removeAttribute(FILTER_APPLIED);

			if (debug) {
				logger.debug("SecurityContextHolder now cleared, as request processing completed");
			}
		}
	}

	public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
		this.forceEagerSessionCreation = forceEagerSessionCreation;
	}
}
  1. ThreadLocal(拓展)
      ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后能够优雅的解决一些实际中的并发问题。
	public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

  首先是set方法。我们可以看到,threadMap中有一个map,但这个map不是我们平时使用的map,而是ThreadLocalMap。ThreadLocalMap是ThreadLocal的一个内部类,是不对外使用的,当使用ThreadLocal存值时,首先获取到当前线程对象,然后获取到当前线程本地的对象,本地变量map,最后将当前使用的ThreadLocal和传入的值放在map中,也就是说,ThreadLocalMap中存储值的key是ThreadLocal对象。这样做的好处是每个线程都对应一个本地变量的map,所以一个线程可以存在多个线程本地变量。

	public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

  接下来是get方法。如果没有执行过set操作,那么从ThreadLocal中拿到的值就是空,这是get方法会返回初始值,也就是调用这里面的初始值的方法(setInitialValue())。ThreadLocal中,这个方法默认返回空,当我们有需要第一次get就能得到一个值时,是可以继承ThreadLocal并且覆盖初始值这个方法的。ThreadLocal是解决线程安全问题的一个很好的思路,它通过为每一个线程提供一个独立的变量副本,解决变量并发访问的冲突问题,在很多情况,ThreadLocal它的同步机制解决线程安全问题会变得非常简单,非常方便,而且程序拥有更高的并发性。当我们使用ThreadLocal的时候,一定要注意当一个线程结束时,一定要把当前ThreadLocal中的信息移除掉。

public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

  系统为我们提供了一个remove方法,我们只要调用这个方法就可以了,这个时候它就会清掉当前线程携带的信息。
3. LogoutFilter
  LogoutFilter只处理注销请求,默认处理的请求为j_spring_security_logout。它的用途是在用户发送注销请求时,销毁用户的session,清空SecurityContextHolder,然后重定向到注销成功页面,可以与RememberMe功能结合,在注销的同时,清空用户的cookie。
4. AbstractAuthenticationProcessingFilter
  AbstractAuthenticationProcessingFilter是处理form登陆的过滤器,与form有关的所有操作都在此进行。默认情况下,它处理的是j_spring_security_check这个请求。这个请求是用户使用form登陆的提交地址,此过滤器执行基本操作时,是通过用户名和密码判断用户是否有效,如果登陆成功,就跳转到成功页面,它可能是登陆之前访问的受保护页面,也可能是默认的成功页面;如果登陆失败,则跳转到失败页面。
5. DefaultLoginPageGeneratingFilter
  DefaultLoginPageGeneratingFilter用来生成一个默认的登陆页面,默认的访问地址为spring _security_login,这个默认登陆页面虽然支持用户输入用户名密码,也支持RememberMe的功能,但是因为很简陋,只能在演示时做个样子,不能在实际项目中使用。
6. BasicAuthenticationFilter
  BasicAuthenticationFilter主要用来进行basic验证,功能与前面的AbstractAuthenticationProcessingFilter很类似,只是验证的方式不同。
7. SecurityContextHolderAwareRequestFilter
  SecurityContextHolderAwareRequestFilter用来包装客户端的请求,目的是在原来请求的基础之上为后续程序提供一些额外的数据。
8. RememberMeAuthenticationFilter
  RememberMeAuthenticationFilter实现的是RememberMe功能,当用户库位中存在RememberMe标记时,它会根据标记,自动实现用户登陆并创建SecurityContext所对应的权限,SpringSecurity中的RememberMe是依赖cookie实现的,当用户在登陆的时候选择使用RememberMe时,系统就会在登陆成功后,为用户生成一个唯一的标识,并将这个标识保存进cookie中,我们可以通过浏览器查看用户电脑中的cookie。
9. AnonymousAuthenticationFilter
  AnonymousAuthenticationFilter是为了保障操作统一性,当用户没有登陆时,默认为用户分配匿名用户的权限(许多项目会关闭匿名用户)。
10. ExceptionTranslationFilter
  ExceptionTranslationFilter是为了处理FilterSecurityIntercepter中抛出的异常,然后将请求重定向到对应页面或返回对应的错误代码。
11. SessionManagementFilter
  SessionManagementFilter是为了防御会话伪造攻击,主要是在用户成功登陆之后,销毁用户当前的session并重新生成一个session。
12. FilterSecurityInterceptor
  FilterSecurityInterceptor包含用户所有的权限控制。它的第一个功能是,如果用户尚未登陆则抛出尚未认证的异常,它的第二个功能是如果用户已登录,但是没有访问当前资源的权限则会抛出拒绝访问的异常,它的第三个功能是如果用户已登录并且具有访问当前内容的权限,则放行。
13. FilterChainProxy
  FilterChainProxy会按照顺序来调用一组filter,使这些filter既能完成验证授权的本职工作,又能享用springIOC的功能并很方便的得到其他依赖的资源。

猜你喜欢

转载自blog.csdn.net/qq_41620800/article/details/84795155