欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Security学习笔记-Filter

程序员文章站 2024-03-19 13:35:22
...
  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的功能并很方便的得到其他依赖的资源。

相关标签: SpringSecurity