最近在學習安全框架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標籤解析