1. 程式人生 > >解決自定義securityMetadataSource不能使用依賴注入的問題,nullpointer問題,空指標問題

解決自定義securityMetadataSource不能使用依賴注入的問題,nullpointer問題,空指標問題

spring3,spring security,MySecurityMetadataSource,自定義securityMetadataSource,依賴注入,註解,注入,空指標異常,nullpointer,service

解決的問題,nullpointer問題,空指標問題我在配置spring3Security3 的時候為了從資料庫管理資源,自定義了securityMetadataSource,但是在類中使用依賴注入的話,載入applicationContext-security.xml的時候,會丟擲使用依賴注入的那一行物件為null,是什麼原因?也就是說提示依賴注入resourcesServiceImpl

nullpointer這裡只貼出applicationContext-security.xmlMySecurityMetadataSource.java的程式碼。網上有很多談到的解決方案,但是都沒有解決實質問題。這裡給出我的解決方法,其實,如同異常所示:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name'mySecurityMetadataSource' defined in file [E:\Program Files\Apache SoftwareFoundation\Tomcat6.0\webapps\testSpringSecure\WEB-INF\classes\com\test\security\MySecurityMetadataSource.class]:Instantiation of bean failed; nested exception is 

org.springframework.beans.BeanInstantiationException: Could not instantiate bean class[com.test.security.MySecurityMetadataSource]: Constructor threw exception;nested exception is java.lang.NullPointerException

    atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(

AbstractAutowireCapableBeanFactory.java:997)

    atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:943)

    atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)

    atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)

    atorg.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)

    atorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)

    atorg.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)

    atorg.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)

    atorg.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:322)

    ...53 more

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class[com.test.security.MySecurityMetadataSource]: Constructor threw exception;nested exception is java.lang.NullPointerException

    atorg.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)

    atorg.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76)

    atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)

    ...61 more

Caused by: java.lang.NullPointerException

    atcom.test.security.MySecurityMetadataSource.loadResourceDefine(MySecurityMetadataSource.java:81)

    atcom.test.security.MySecurityMetadataSource.<init>(MySecurityMetadataSource.java:51)

    atsun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

    atsun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)

    atsun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)

    atjava.lang.reflect.Constructor.newInstance(Constructor.java:513)

    atorg.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)

    ...63 more

問題出在com.test.security.MySecurityMetadataSource.loadResourceDefine(MySecurityMetadataSource.java:81)上面。

網上給出的大部分解決方法是在xml裡面配置強制注入resourcesServiceImpl,但是這樣做很不靈活,而且由於大部分程式碼都已經用了註解來配置注入,所以不建議使用xml再來配置注入。那麼究竟是什麼原因呢?其實答案很簡單,就是在MySecurityMetadataSource.java中應該把兩個自定義構造器去掉。如同我在下面的註釋所說:

不要使用自定義構造方法,否則會導致注入會出錯?應該是因為構造器中呼叫了loadResourceDefine方法,先於service注入,所以丟擲異常。

我們這裡並不需要自定義構造器,因為在使用註解注入時,注入會自動進行,當你使用了自定義構造器,由於裡面呼叫了loadResourceDefine();而構造器在注入發生前就被呼叫了,所以就會先執行loadResourceDefine();,此時注入還沒進行,因而就會丟擲空指標異常了。

applicationContext-security.xml: 

<?xml version="1.0"encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

    xmlns:beans="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd

                       http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.0.xsd">

<!--  

    <global-method-security pre-post-annotations="enabled"/>  -->

<global-method-security secured-annotations="enabled" jsr250-annotations="enabled"></global-method-security>

<http access-denied-page="/access_denied.jsp"  use-expressions="true"  entry-point-ref="authenticationProcessingFilterEntryPoint">

<!-- 該路徑下的資源不用過濾 -->

<intercept-url pattern="/js/**" filters="none"/>

<!-- <logout invalidate-session="true" logout-success-url="/login.jsp"/>-->

<logout invalidate-session="true" logout-url="/j_spring_security_logout"success-handler-ref="customLogoutSuccessHandler"  />

<!-- 實現免登陸驗證 -->

<remember-me />

<!--

       <form-login login-page="/login.jsp"default-target-url="/frame.jsp"always-use-default-target="true"/>

        -->

<session-management invalid-session-url="/login.jsp">

<concurrency-control max-sessions="100" error-if-maximum-exceeded="false"/>

</session-management>

<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"  />

<custom-filter ref="securityFilter"before="FILTER_SECURITY_INTERCEPTOR"/>

</http>

<!-- 未登入的切入點 -->

<beans:bean id="authenticationProcessingFilterEntryPoint"class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">

<beans:property name="loginFormUrl"value="/login.jsp"></beans:property>

</beans:bean>

<!-- 退出登入的切入點 -->

<beans:bean id="customLogoutSuccessHandler" class="com.test.security.CustomLogoutSuccessHandler">

</beans:bean>

<!-- 登入驗證器 -->

<beans:bean id="loginFilter"

       class="com.test.security.MyUsernamePasswordAuthenticationFilter">

<!-- 處理登入 -->

<beans:property name="filterProcessesUrl"value="/j_spring_security_check"></beans:property>

<beans:property name="authenticationSuccessHandler"ref="loginLogAuthenticationSuccessHandler"></beans:property>

<beans:property name="authenticationFailureHandler"ref="simpleUrlAuthenticationFailureHandler"></beans:property>

<beans:property name="authenticationManager"ref="myAuthenticationManager"></beans:property>

</beans:bean>

<beans:bean id="loginLogAuthenticationSuccessHandler"

        class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">

<beans:property name="defaultTargetUrl"value="/index.jsp"></beans:property>

</beans:bean>

<beans:bean id="simpleUrlAuthenticationFailureHandler"

        class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">

<beans:property name="defaultFailureUrl"value="/login.jsp"></beans:property>

</beans:bean>

<!-- 認證過濾器 -->

<beans:bean id="securityFilter"class="com.test.security.MySecurityFilter">

<!-- 使用者擁有的許可權 -->

<beans:property name="authenticationManager" ref="myAuthenticationManager" />

<!-- 使用者是否擁有所請求資源的許可權 -->

<beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />

<!-- 資源與許可權對應關係 -->

<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />

</beans:bean>

<!-- 實現了UserDetailsServiceBean -->

<authentication-manager alias="myAuthenticationManager">

<authentication-provider user-service-ref="myUserDetailServiceImpl"/>

</authentication-manager>

</beans:beans>

MySecurityMetadataSource: 

package com.test.security;

import java.util.ArrayList;

import java.util.Collection;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import java.util.Set;

import java.util.Map.Entry;

import javax.annotation.PostConstruct;

import javax.annotation.Resource;

import org.springframework.security.access.ConfigAttribute;

import org.springframework.security.access.SecurityConfig;

import org.springframework.security.web.FilterInvocation;

import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import org.springframework.stereotype.Component;

import com.test.bean.Resources;

import com.test.dao.IResourcesDao;

import com.test.service.IResourcesService;

//1 載入資源與許可權的對應關係

@Component("mySecurityMetadataSource")

public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

/*不要使用自定義構造方法,否則導致注入會出錯?應該是構造器中呼叫了loadResourceDefine方法,先於service注入,所以丟擲異常。

public MySecurityMetadataSource() {

loadResourceDefine();

}

//由spring呼叫

public MySecurityMetadataSource(IResourcesService resourcesServiceImpl) {

this.resourcesServiceImpl = resourcesServiceImpl;

loadResourceDefine();

}*/

@Resource

private IResourcesService resourcesServiceImpl;

private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

public Collection<ConfigAttribute> getAllConfigAttributes() {

// TODO Auto-generated method stub

return null;

}

public boolean supports(Class<?> clazz) {

// TODO Auto-generated method stub

return true;

}

//載入所有資源與許可權的關係

@PostConstruct

private void loadResourceDefine() {

if(resourceMap == null) {

resourceMap = new HashMap<String, Collection<ConfigAttribute>>();

List<Resources> resources = this.resourcesServiceImpl.findAll();

//List<Resources> resources = new ArrayList();

for (Resources resource : resources) {

Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();

                                //以許可權名封裝為Spring的security Object

ConfigAttribute configAttribute = new SecurityConfig(resource.getName());

configAttributes.add(configAttribute);

resourceMap.put(resource.getUrl(), configAttributes);

}

}

Set<Entry<String, Collection<ConfigAttribute>>> resourceSet = resourceMap.entrySet();

Iterator<Entry<String, Collection<ConfigAttribute>>> iterator = resourceSet.iterator();

System.out.println("MySecurityMetadataSource:loadResourceDefine()");

}

//返回所請求資源所需要的許可權

public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

System.out.println("MySecurityMetadataSource:getAttributes()");

String requestUrl = ((FilterInvocation) object).getRequestUrl();

System.out.println("requestUrl is " + requestUrl);

if(resourceMap == null) {

loadResourceDefine();

}

Collection<ConfigAttribute> c = resourceMap.get(requestUrl);

System.out.println("MySecurityMetadataSource Collection<ConfigAttribute>:"+c);

return c;

}

}