spring security
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring security相关的知识,希望对你有一定的参考价值。
这个需要从DelegatingFilterProxy类说起,从名字来看是个代理类,也就是说它并不是filter的实际实现,而且他从属于 org.springframework.web.filter这个包,也就是说它没有和springsecurity强绑定,其实从他的dofilter方法不难发现它是通过bean名去springcontext中取出相应的filter bean,然后执行dofilter。为啥不直接写成filter呢,可能设计者就是想设计的灵活吧,根据配置的bean名从springcontext取出。
DelegatingFilterProxy需要在web.xml里配置,和其他filter没有区别
<!-- SpringSecurity需要的filter --><filter> <filter-name>spring-security</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>abcFilterChain</param-value> </init-param></filter><filter-mapping> <filter-name>spring-security</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
这里init-param里的param-name和param-value可以不用配置,只要配置filter-name为bean名也可以,程序中找不到param-name参数就会使用filter-name,一般我们使用springsecurity时,直接将filter-name写为springSecurityFilterChain,因为springsecurity在解析spring-security.xml时会默认注册springSecurityFilterChain这个bean。这个得从标签解析说起,Spring Security的标签解析由org.springframework.security.config.SecurityNamespaceHandler来处理。该类实现接口:NamespaceHandler,Spring中自定义标签都要实现该接口,这个接口有三个方法init、parse、decorate,其中init用于自定义标签的初始化,parse用于解析标签,decorate用于装饰。SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作。打开SecurityNamespaceHandler源码。
public void init() { loadParsers();} private void loadParsers() { // Parsers parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser()); parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser()); parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser()); parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser()); parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser()); parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser()); parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser()); parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser()); parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser()); // Only load the web-namespace parsers if the web classes are available if (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) { parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser()); parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser()); parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser()); parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser()); filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator(); } if (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) { parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER, new WebSocketMessageBrokerSecurityBeanDefinitionParser()); } }
上面可以看出SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作,并且http的标签解析类注册代码为:
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
在该类中parse方法
public BeanDefinition parse(Element element, ParserContext pc) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)); pc.pushContainingComponent(compositeDef); // 这里创建了listFactoryBean实例和springSecurityFilterChain实例,详解移步3.2.1 registerFilterChainProxyIfNecessary(pc, pc.extractSource(element)); // 获取listFactoryBean实例 BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS); List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue(); // 创建过滤器链代码,注意这里添加的filter会在FilterChainProxy的doFilterInternal中使用。 filterChains.add(createFilterChain(element, pc)); pc.popAndRegisterContainingComponent(); return null;}
首先看registerFilterChainProxyIfNecessary方法
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) { // 判断是否已经注册了FILTER_CHAIN_PROXY,若已经注册则直接返回,也就是说用户可以自定义BeanIds.FILTER_CHAIN_PROXY bean,如果那样的话,系统就不自动 注册那个bean了 if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) { return; } // 注册ListFactoryBean,也就是把BeanIds.FILTER_CHAINS和一个beandefinition绑定,springcontext里都是这样保持一个map,然后调用getbean时会根据 bean名找到beandefinition,然后根据beanDefinition创建bean实例,当然如果web.xml里配置了ContextLoaderListener,他会在tomcat启动后初始化springcontext 并创建单例的bean,这个具体参见ContextLoaderListener的分析。 BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class); listFactoryBean.getPropertyValues().add("sourceList", new ManagedList()); pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS)); // 注册FilterChainProxy BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class); fcpBldr.getRawBeanDefinition().setSource(source); fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);//这里将BeanIds.FILTER_CHAINS设置为FilterChainProxy类的初始化参数 //查看FilterChainProxy类的doFilterInternal->getFilters会用到这个参数,也就是filterchains,之后会执行这个chain里的filter, //那么这个chain里的filter从哪注册呢,可以看看上面parse方法里的倒数第三句。 fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class)); BeanDefinition fcpBean = fcpBldr.getBeanDefinition(); pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY)); // 此处为FILTER_CHAIN_PROXY取别名为springSecurityFilterChain pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);}
接着看createFilterChain
// 创建过滤器链的代码 private BeanReference createFilterChain(Element element, ParserContext pc) { // 判断是否需要SpringSecurity拦截 boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED)); if (!secured) { // 如果没pattern并且配置request-matcher-ref为空 添加错误信息 if (!StringUtils.hasText(element.getAttribute(ATT_PATH_PATTERN)) && !StringUtils.hasText(ATT_REQUEST_MATCHER_REF)) { pc.getReaderContext().error("The '" + ATT_SECURED + "' attribute must be used in combination with" + " the '" + ATT_PATH_PATTERN + "' or '" + ATT_REQUEST_MATCHER_REF + "' attributes.", pc.extractSource(element)); } for (int n = 0; n < element.getChildNodes().getLength(); n++) { if (element.getChildNodes().item(n) instanceof Element) { // 如果element有子节点并且instanceofElement 添加错误信息 pc.getReaderContext().error("If you are using <http> to define an unsecured pattern, " + "it cannot contain child elements.", pc.extractSource(element)); } } return createSecurityFilterChainBean(element, pc, Collections.emptyList()); } final BeanReference portMapper = createPortMapper(element, pc); final BeanReference portResolver = createPortResolver(portMapper, pc); ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>(); BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders); boolean forceAutoConfig = isDefaultHttpConfig(element); // HttpConfigurationBuilder创建过滤器链,详情移步3.2.2.1 HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager); // AuthenticationConfigBuilder创建过滤器链,详情移步3.2.2.2 AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler()); httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers()); httpBldr.setEntryPoint(authBldr.getEntryPointBean()); httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean()); authenticationProviders.addAll(authBldr.getProviders()); List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>(); // 添加过滤器到unorderedFilterChain unorderedFilterChain.addAll(httpBldr.getFilters()); unorderedFilterChain.addAll(authBldr.getFilters()); unorderedFilterChain.addAll(buildCustomFilterList(element, pc)); // 对过滤器链进行排序,详细分析请移步3.2.2.3 Collections.sort(unorderedFilterChain, new OrderComparator()); // 对过滤器链正确性检查 checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element)); // The list of filter beans List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>(); for (OrderDecorator od : unorderedFilterChain) { filterChain.add(od.bean); } // 创建过滤器链,详情移步3.2.2.4 return createSecurityFilterChainBean(element, pc, filterChain);}
以上是关于spring security的主要内容,如果未能解决你的问题,请参考以下文章
oauth2 spring-security 如果您在请求令牌或代码之前登录
Spring Security:如何获取自动渲染的登录页面代码?