最近在学习安全框架spring Security,想弄清楚其中实现的具体步骤,于是下定决心,研究一下Spring Security源码,这篇博客的目的是想把学习过程记录下来。学习过程中主要参考了http://dead-knight.iteye.com/category/220917大神的博客,然后在其基础上,进行更详细的说明
authentication-manager在标签配置文件中的定义一般如下:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsManager"/>
</authentication-manager>
1.authentication-manager标签解析类为:org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser,具体解析方法parse的代码为:
public BeanDefinition parse(Element element, ParserContext pc) {
String id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
// 判断是否有注册过BeanIds.AUTHENTICATION_MANAGER
if (pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)) {
// 如果BeanIds.AUTHENTICATION_MANAGER已经被注册 添加错误信息
pc.getReaderContext().warning("Overriding globally registered AuthenticationManager", pc.extractSource(element));
}
// 配置id默认值为BeanIds.AUTHENTICATION_MANAGER
id = BeanIds.AUTHENTICATION_MANAGER;
}
pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)));
// 构建ProviderManager的BeanDefinition
BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
// 获取ATT_ALIAS属性值
String alias = element.getAttribute(ATT_ALIAS);
List<BeanMetadataElement> providers = new ManagedList<BeanMetadataElement>();
NamespaceHandlerResolver resolver = pc.getReaderContext().getNamespaceHandlerResolver();
// 获取authentication-manager的子节点
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node instanceof Element) {
Element providerElt = (Element) node;
// 判断是否配置了ATT_REF属性
if (StringUtils.hasText(providerElt.getAttribute(ATT_REF))) {
// 若配置了ATT_REF属性,则不允许配置其他属性否则添加错误信息
if (providerElt.getAttributes().getLength() > 1) {
pc.getReaderContext().error("authentication-provider element cannot be used with other attributes " + "when using 'ref' attribute", pc.extractSource(element));
}
// 判断是否有子节点
NodeList providerChildren = providerElt.getChildNodes();
for (int j = 0; j < providerChildren.getLength(); j++) {
// 如果有子节点则添加错误信息
if (providerChildren.item(j) instanceof Element) {
pc.getReaderContext().error("authentication-provider element cannot have child elements when used " + "with 'ref' attribute", pc.extractSource(element));
}
}
providers.add(new RuntimeBeanReference(providerElt.getAttribute(ATT_REF)));
}else
// 如果没有ATT_REF属性,则通过子标签的解析类完成标签解析 详情移步2
BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc);
Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition");
String providerId = pc.getReaderContext().generateBeanName(provider);
// 注册provider的BeanDefinition
pc.registerBeanComponent(new BeanComponentDefinition(provider, providerId));
// 添加注册过的bean到provider集合中
providers.add(new RuntimeBeanReference(providerId));
}
}
}
// 如果providers为空 添加NullAuthenticationProvider
if (providers.isEmpty()) {
providers.add(new RootBeanDefinition(NullAuthenticationProvider.class));
}
// 为providerManagerBldr注入参数
providerManagerBldr.addConstructorArgValue(providers);
// 判断ATT_ERASE_CREDENTIALS属性是否为false
if ("false".equals(element.getAttribute(ATT_ERASE_CREDENTIALS))) {
providerManagerBldr.addPropertyValue("eraseCredentialsAfterAuthentication", false);
}
// 构造DefaultAuthenticationEventPublisher的BeanDefinition
BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);
String pubId = pc.getReaderContext().generateBeanName(publisher);
pc.registerBeanComponent(new BeanComponentDefinition(publisher, pubId));
providerManagerBldr.addPropertyReference("authenticationEventPublisher", pubId);
// 注册ProviderManager的bean
pc.registerBeanComponent(new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), id));
// 判断是否有别名,有则注册别名
if (StringUtils.hasText(alias)) {
pc.getRegistry().registerAlias(id, alias);
pc.getReaderContext().fireAliasRegistered(id, alias, pc.extractSource(element));
}
pc.popAndRegisterContainingComponent();
return null;
}
通过上面的代码片段,能够知道authentication-manager标签解析的步骤是:
1.构造ProviderManager的BeanDefinition
2.循环authentication-manager的子标签,构造provider的BeanDefinition,并添加到providers集合中
3.将第2步的providers设置为ProviderManager的providers属性
4.构造DefaultAuthenticationEventPublisher的BeanDefinition,并设置为ProviderManager的属性authenticationEventPublisher
5.通过registerBeanComponent方法完成bean的注册任务
2.子标签解析类为:org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser,具体解析方法parse的代码为:
public BeanDefinition parse(Element element, ParserContext pc) {
// 首先构造DaoAuthenticationProvider的BeanDefinition
RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);
authProvider.setSource(pc.extractSource(element));
// 获取password-encoder子标签
Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);
// 判断是否有password-encoder
if (passwordEncoderElt != null) {
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, pc);
authProvider.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
// 判断是否有salt-source标签
if (pep.getSaltSource() != null) {
authProvider.getPropertyValues().addPropertyValue("saltSource", pep.getSaltSource());
}
}
// 获取user-service标签
Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
// 获取jdbc-user-service标签
if (userServiceElt == null) {
userServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
}
// 获取ldap-user-service标签
if (userServiceElt == null) {
userServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);
}
// 获取user-service-ref属性
String ref = element.getAttribute(ATT_USER_DETAILS_REF);
if (StringUtils.hasText(ref)) {
if (userServiceElt != null) {
// 如果配置了user-service-ref属性 且有子标签 则添加错误信息
pc.getReaderContext().error("The " + ATT_USER_DETAILS_REF + " attribute cannot be used in combination with child" + "elements '" + Elements.USER_SERVICE + "', '" + Elements.JDBC_USER_SERVICE + "' or '" + Elements.LDAP_USER_SERVICE + "'", element);
}
authProvider.getPropertyValues().add("userDetailsService", new RuntimeBeanReference(ref));
}else {
// 利用子标签创建UserDetailsService
if (userServiceElt != null) {
pc.getDelegate().parseCustomElement(userServiceElt, authProvider);
} else {
// 添加错误信息
pc.getReaderContext().error("A user-service is required", element);
}
// 查看是否配置缓存属性
String cacheRef = userServiceElt.getAttribute(AbstractUserDetailsServiceBeanDefinitionParser.CACHE_REF);
if (StringUtils.hasText(cacheRef)) {
authProvider.getPropertyValues().addPropertyValue("userCache", new RuntimeBeanReference(cacheRef));
}
}
return authProvider;
}
3.替换配置
如果不使用Spring Security标签来配置Spring Security,适用Spring基础标签标签,如何配置?请参考authentication-manager标签解析