• Spring Security 介绍


    Spring Security介绍

    • 开源
    • 提供企业级的安全认证和授权

    Spring安全拦截器

    • 认证管理器

      • 认证模式

        • Basic

          HTTP 1.0中使用的认证方法,使用用户名和密码Base64编码的方式

          浏览器弹出对话框,用户输入用户名和密码,加入头部

          无状态

          安全性不足

        • Digest

          解决安全性问题

          浏览器对用户名和密码请求方法,URL等进行MD5运算,发送到服务器

          服务器获取到用户名密码,请求方法,URL等MD5运算,查看是否相等

          安全性问题依然存在

        • X.509

    • 访问决策管理器

    • 运行身份管理器

    常用权限拦截器

    Spring-Security拦截器链流程图

    介绍几个常用的Filter

    1. SecurityContextPersistenceFilter

      基于ThreadLocal

    2. LogoutFilter

      发送注销请求时,清空用户的session,cookie,重定向

    3. AbstractAuthenticationProcessingFilter

      用户登录的请求

    4. DefaultLoginPageGeneratingFilter

      生成登录页面

    5. BasicAuthencationFilter

    6. SecurityContextHolderAwareRequestFilter

      包装,提供额外的数据

    7. RememberMeAuthemticationFilter

      提供RememberMe功能,当cookie中存在rememberme时,登录成功后生成唯一cookie

    8. ExceptionTranlationFilter

      请求到对应的页面,响应异常

      ExceptionTranslationFilter异常处理过滤器,该过滤器用来处理在系统认证授权过程中抛出的异常(也就是下一个过滤器FilterSecurityInterceptor),主要是 处理 AuthenticationExceptionAccessDeniedException

    9. SessionManagerFilter

    10. FilterSecurityInterceptor(授权)

      用户没有登录,抛出未登录异常

      用户登录,但是没有权限访问该资源,抛出拒绝访问的异常

      用户登录,有权限,则放行

      此过滤器为认证授权过滤器链中最后一个过滤器,该过滤器之后就是请求真正的 服务

    Filter如何执行

    FilterChainProxy会按照顺序调用一组Filter,完成授权验证

    请求首先经过Servlet Filter链,这里面包含6个Filter

    可以看到如下的一个Filter

    ApplicationFilterConfig[name=springSecurityFilterChain, filterClass=org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1]

    这个就是Spring Security Filter的入口,它的类型是DelegationFilterProxy

    那么,这个DelegationFilterProxy到底执行了什么样的操作呢?可以简单看一下源码

    重点看一下部分的源码:

    public class DelegatingFilterProxy extends GenericFilterBean {
        
        // 类中的属性
        @Nullable
    	private String contextAttribute;
    
    	@Nullable
    	private WebApplicationContext webApplicationContext;
    
    	@Nullable
    	private String targetBeanName;
    
    	private boolean targetFilterLifecycle = false;
    
        
        /**
        * 重点在于这个属性
        */
    	@Nullable
    	private volatile Filter delegate;
    
    	private final Object delegateMonitor = new Object();
    
        @Override
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
    			throws ServletException, IOException {
    
    		// Lazily initialize the delegate if necessary.
    		Filter delegateToUse = this.delegate;
    		if (delegateToUse == null) {
    			synchronized (this.delegateMonitor) {
    				delegateToUse = this.delegate;
    				if (delegateToUse == null) {
    					WebApplicationContext wac = findWebApplicationContext();
    					if (wac == null) {
    						throw new IllegalStateException("No WebApplicationContext found: " +
    								"no ContextLoaderListener or DispatcherServlet registered?");
    					}
    					delegateToUse = initDelegate(wac);
    				}
    				this.delegate = delegateToUse;
    			}
    		}
    
    		// Let the delegate perform the actual doFilter operation.
    		invokeDelegate(delegateToUse, request, response, filterChain);
    	}
        // .......
        
        /**
    	 * Initialize the Filter delegate, defined as bean the given Spring
    	 * application context.
    	 * <p>The default implementation fetches the bean from the application context
    	 * and calls the standard {@code Filter.init} method on it, passing
    	 * in the FilterConfig of this Filter proxy.
    	 
    	 * @param wac the root application context
    	 * @return the initialized delegate Filter
    	 * @throws ServletException if thrown by the Filter
    	 */
    	protected Filter initDelegate(WebApplicationContext wac) throws ServletException 	{
    		String targetBeanName = getTargetBeanName();
    		Assert.state(targetBeanName != null, "No target bean name set");
    		Filter delegate = wac.getBean(targetBeanName, Filter.class);
    		if (isTargetFilterLifecycle()) {
    			delegate.init(getFilterConfig());
    		}
    		return delegate;
    	}
        
        // 调用delegate的doFilter方法
        protected void invokeDelegate(
    			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
    			throws ServletException, IOException {
    
    		delegate.doFilter(request, response, filterChain);
    	}
    }
    

    根据以上代码的注释我们可以看出大概的执行流程

    那类中的Filter delegate是什么呢?断点调式运行可以找到以下东西

    这是Spring Security 提供的一个FilterChainProxy,关注其中的关键源码

    public class FilterChainProxy extends GenericFilterBean {
        
        // 包含一组SecurityFilterChain
        private List<SecurityFilterChain> filterChains;
        
        @Override
    	public void doFilter(ServletRequest request, ServletResponse response,
    			FilterChain chain) throws IOException, ServletException {
    		boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
    		if (clearContext) {
    			try {
    				request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
    				doFilterInternal(request, response, chain);
    			}
    			finally {
    				SecurityContextHolder.clearContext();
    				request.removeAttribute(FILTER_APPLIED);
    			}
    		}
    		else {
    			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);
    		
            
            // 根据请求获取匹配的一组Filter
            // 这里返回的Filter就是上述的那些AuthenticationFilter,比如UsernamePasswordAuthenticationFilter
    		List<Filter> filters = getFilters(fwRequest);
    
    		if (filters == null || filters.size() == 0) {
    			if (logger.isDebugEnabled()) {
    				logger.debug(UrlUtils.buildRequestUrl(fwRequest)
    						+ (filters == null ? " has no matching filters"
    								: " has an empty filter list"));
    			}
    
    			fwRequest.reset();
    
    			chain.doFilter(fwRequest, fwResponse);
    
    			return;
    		}
    
    		VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
    		vfc.doFilter(fwRequest, fwResponse);
    	}
        
        /**
    	 * Returns the first filter chain matching the supplied URL.
    	 *
    	 * @param request the request to match
    	 * @return an ordered array of Filters defining the filter chain
    	 */
    	private List<Filter> getFilters(HttpServletRequest request) {
    		for (SecurityFilterChain chain : filterChains) {
    			if (chain.matches(request)) {
    				return chain.getFilters();
    			}
    		}
    
    		return null;
    	}
    
    }
    

    到此,我们可以总结出如下的流程图:

  • 相关阅读:
    Unittest单元测试
    关于压力机设备的一些题
    原生 JS 实现 HTML 转 Markdown ,(html2md.js 或 html2markdown.js)
    【滚动更新】C++ 八股文选集(没代码,纯应试)
    【Example】C++ STL 常用容器概述
    权限接口写法
    使用vuecli3创建项目
    vue通过路由控制来实现的权限管理
    【更新中】程序设计实习笔记
    kserve predict api v2
  • 原文地址:https://www.cnblogs.com/watertreestar/p/11780314.html
Copyright © 2020-2023  润新知