spring3MVC +spring security3 关于扩展UserDetailsService 无法注入service的疑问.

2019-03-25 13:52|来源: 网路

问题描述:
我想用的spring3MVC+spring security3 实现登录权限的扩展.即扩展UserDetailsService 以达到从持久层上控制登录信息.
但是现在的问题是我启动服务器后,登录时在UserDetailsService 中 service层的方法无法注入.
如下图

注意图中红线的地方. accountService为null.表示注入失败.

所以我想请教下大家:为什么会出现accountService为null 的问题.

我把主要的代码贴出来并在最后提供例子的下载.

首先是项目文件结构.





下面这段是继承UserDetailsService以实现持久层关联登录.


@Transactional(readOnly = true)
public class UserDetailsServiceImpl implements UserDetailsService {

	private static final Logger logger = LoggerFactory
			.getLogger(UserDetailsServiceImpl.class);

	@Resource(name = "accountService")
	private IAccountService accountService;

	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {

		logger.info("username:" + username);

		User user = accountService.getUserInfo(username);

		if (user == null) {
			throw new UsernameNotFoundException("用户" + username + "不存在!");
		}

		Set<GrantedAuthority> grantedAuths = obtainGrantedAuthorities(user);

		boolean enabled = true;
		boolean accountNonExpired = true;
		boolean credentialsNonExpired = true;
		boolean accountNonLocked = true;

		UserDetails userDetails = new org.springframework.security.core.userdetails.User(
				username, user.getPassword(), enabled, accountNonExpired,
				credentialsNonExpired, accountNonLocked, grantedAuths);
		return userDetails;
	}

	private Set<GrantedAuthority> obtainGrantedAuthorities(User user) {

		Set<GrantedAuthority> authSet = Sets.newHashSet();

		for (Role role : user.getRoleList()) {
			for (Authority authority : role.getAuthorityList()) {
				logger.info("authority is :" + authority.getPrefixedName());
				authSet.add(new GrantedAuthorityImpl(authority
						.getPrefixedName()));
			}
		}
		return authSet;

	}

}



service接口
public interface IAccountService {
	
	/**
	 * 根据用户登录名获得对应的权限信息.
	 */
	public User getUserInfo(String userName);

}


实现IAccountService的类


@Service("accountService")
@Transactional
public class AccountService implements IAccountService {

	@Resource(name="accountDao")
	private IAccountDao dao;

	private static final Logger logger = LoggerFactory
			.getLogger(AccountService.class);

	public User getUserInfo(String userName) {

		// 根据登录名(userName)获得对应的信息
		User user = dao.getUserByloginName(userName);

		// 根据登录名获得对应的角色名.如 用户admin拥有"用户","管理员"这两个角色
		List<Role> roleList = dao.getRoleByUserName(userName);
		for (int i = 0; i < roleList.size(); i++) {

			// 根据角色的ID查询出每个角色对应的权限.
			List<Authority> authorityList = dao.getAuthorityByRoleId(roleList
					.get(i).getId());

			Role role = new Role();

			// 将查询出来的权限信息放入Role中的authorityList.
			role.setAuthorityList(authorityList);

			// 对象role放入集合中
			roleList.add(role);

		}

		// 将查询出来的角色信息(包含各个角色信息对应的权限信息)装入User里的roleList中.
		user.setRoleList(roleList);
		return user;
	}

}


IAccountDao接口
public interface IAccountDao {

	/**
	 * 根据loginName获得对应用户信息
	 * 
	 * @return
	 */
	public User getUserByloginName(String loginName);

	/**
	 * 根据登录名获得对应的角色.
	 * 
	 * @param userName
	 * @return
	 */
	public List<Role> getRoleByUserName(String userName);

	/**
	 * 根据角色ID获得对应的权限ID
	 * 
	 * @param roleId
	 * @return
	 */
	public List<Authority> getAuthorityByRoleId(int roleId);

}


实现IAccountDao接口的类



@Repository("accountDao")
public class AccountDao implements IAccountDao {

	private static final Logger logger = LoggerFactory.getLogger(AccountDao.class);

	private SimpleJdbcTemplate jdbcTemplate;

	@Resource(name = "dataSource")
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
	}

	public User getUserByloginName(String loginName) {
		String sql = "SELECT id,email,login_name,name,password FROM acct_user WHERE login_name = ?";
		return jdbcTemplate.queryForObject(sql,
				new BeanPropertyRowMapper<User>(User.class),
				new Object[] { loginName });
	}

	public List<Role> getRoleByUserName(String userName) {
		String sql = "SELECT acct_role.id,acct_role.name FROM acct_user LEFT JOIN acct_user_role ON acct_user_role.user_id=acct_user.id  LEFT JOIN acct_role ON acct_role.id=acct_user_role.role_id  WHERE acct_user.login_name = ?";
		return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Role>(
				Role.class), new Object[] { userName });
	}

	public List<Authority> getAuthorityByRoleId(int roleId) {
		String sql = "SELECT acct_authority.id,acct_authority.name,acct_authority.url FROM acct_authority LEFT JOIN acct_role_authority ON acct_authority.id=acct_role_authority.authority_id LEFT JOIN acct_role ON acct_role.id=acct_role_authority.role_id WHERE acct_role.id= ?";
		return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Authority>(
				Authority.class), new Object[] { roleId });
	}




然后是springsecurity3的配置文件


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:sec="http://www.springframework.org/schema/security"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/security 
		http://www.springframework.org/schema/security/spring-security-3.0.xsd"
	default-lazy-init="true">


	<sec:http auto-config="true" use-expressions="true"
		access-denied-page="/denied">

		<sec:intercept-url pattern="/resources/**" filters="none" />

		<sec:intercept-url pattern="/" access="isAuthenticated()" />

		<sec:intercept-url pattern="/login" access="permitAll" />
		<!-- <sec:intercept-url pattern="/" access="hasRole('ROLE_ADMIN')" /> -->
		<sec:form-login login-page="/login"
			authentication-failure-url="/login?error=1" default-target-url="/" />

		<sec:logout invalidate-session="true" logout-success-url="/login"
			logout-url="/logout" />
	</sec:http>

	<sec:authentication-manager>
		<sec:authentication-provider
			user-service-ref="customUserDetailsService">
			<sec:password-encoder hash="plaintext" />
		</sec:authentication-provider>
	</sec:authentication-manager>

	<bean id="customUserDetailsService" class="com.sjax.myapp.service.UserDetailsServiceImpl" />

</beans>




问题补充: web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets 
		and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		/WEB-INF/spring/application-security.xml
		/WEB-INF/spring/application-dataSource.xml
		/WEB-INF/spring/root-context.xml
		</param-value>
	</context-param>

	

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>


<!-- Character Encoding filter -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	
	<!-- SpringSecurity filter -->
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	
	
	
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	

	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
	</filter-mapping>
	
	
	<!--Spring的ApplicationContext 载入 -->
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- Spring 刷新Introspector防止内存泄露 -->
	<listener>
		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
	</listener>
	
	<!-- session超时定义,单位为分钟 -->
	<session-config>
		<session-timeout>20</session-timeout>
	</session-config>
	
	
	<!-- 出错页面定义 -->
	<error-page>
		<exception-type>java.lang.Throwable</exception-type>
		<location>/common/500.jsp</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/common/500.jsp</location>
	</error-page>
	<error-page>
		<error-code>404</error-code>
		<location>/common/404.jsp</location>
	</error-page>
	<error-page>
		<error-code>403</error-code>
		<location>/common/403.jsp</location>
	</error-page>
</web-app>

问题补充:还是无法注入,但是其他controller是可以注入的.唯独这个UserDetailsService里无法注入~~
顺便把报错信息贴出来


java.lang.NullPointerException
	com.sjax.myapp.service.UserDetailsServiceImpl.loadUserByUsername(UserDetailsServiceImpl.java:37)
	com.sjax.myapp.service.UserDetailsServiceImpl$$FastClassByCGLIB$$9f415683.invoke(<generated>)
	net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
	org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
	org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
	org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
	com.sjax.myapp.service.UserDetailsServiceImpl$$EnhancerByCGLIB$$fcc4b1b7.loadUserByUsername(<generated>)
	org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:86)
	org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:129)
	org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:130)
	org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:48)
	org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:148)
	org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:48)
	org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:97)
	org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
	org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
	org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
	org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:169)
	org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
	org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)

问题补充:事务
archy123 写道
事务注解去掉试试。。

还是不行..

问题补充:
archy123 写道
@Transactional(readOnly = true)  

去掉了这个,还是报错
accountService注入不成功.值为null

相关问答

更多
  • 我不知道你的权限是怎么控制的,比如一个新建人员的按钮,你把他新建的权限给删除了,你是怎么控制那个按钮的,security在jsp中有自己的标签,你可以用那个标签来判断用户是否有该权限,给你个例子 AUTH_4010这个是用户的权限,你的新建按钮的权限是什么你填上去就好了
  • aopalliance-1.0.jar aspectjrt-1.5.4.jar aspectjweaver.jar commons-codec-1.3.jar commons-collections-3.2.jar commons-logging-1.1.1.jar jstl-1.1.2.jar log4j-1.2.14.jar spring-aop-2.0.8.jar spring-beans-2.0.8.jar spring-context-2.0.8.jar spring-core-2.0.8.jar ...
  • 如果有多个filter的话,会根据配置文件中filter的配置顺子组成一个过滤器链(FilterChain),所有的奥秘都在Filter的FilterChain中。服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。执行的顺序为,执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.doFilt ...
  • aopalliance-1.0.jar aspectjrt-1.5.4.jar aspectjweaver.jar commons-codec-1.3.jar commons-collections-3.2.jar commons-logging-1.1.1.jar jstl-1.1.2.jar log4j-1.2.14.jar spring-aop-2.0.8.jar spring-beans-2.0.8.jar spring-context-2.0.8.jar spring-core-2.0.8.jar ...
  • 我不知道你的权限是怎么控制的,比如一个新建人员的按钮,你把他新建的权限给删除了,你是怎么控制那个按钮的,security在jsp中有自己的标签,你可以用那个标签来判断用户是否有该权限,给你个例子 AUTH_4010这个是用户的权限,你的新建按钮的权限是什么你填上去就好了
  • 如果有多个filter的话,会根据 配置文件中filter的配置顺子组成一个过滤器链(FilterChain),所有的奥秘都在Filter的FilterChain中。 服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。执行的顺序为,执行第一个过滤器的chain.doFilter() 之前的 代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.do ...
  • spring
  • 听起来是csrf过滤器。 在Spring Security 4.x中,它默认激活: 从Spring Security 3.x迁移到4.x. 如果你总是得到HTTP 403,这可能是个问题。 尝试在security:http元素中禁用设置: It sounds to be the csrf filter. In Spring Security 4.x it is activated by default: Migrating from Spring Secur ...
  • 登录页面中的参数名称有误,请参阅 FormLoginConfigurer#usernameParameter : 执行身份验证时查找用户名的HTTP参数。 默认为“用户名”。 FormLoginConfigurer#passwordParameter : 执行身份验证时查找密码的HTTP参数。 默认为“密码”。 更改登录页面中参数的名称或更改SecurityConfig的名称。 The parameter names in your login page are wrong, see FormLoginCo ...