Java 如何配置 Spring-Security 以访问数据库中的用户详细信息?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31965252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:51:02  来源:igfitidea点击:

How to configure Spring-Security to access user details in database?

javaspringhibernatespring-mvcspring-security

提问by Daniel Newtown

I am puzzled with SpringSecurity. There are many ways to implement a simple thing and I mixed them all up.

我对 SpringSecurity 感到困惑。有很多方法可以实现一个简单的事情,我把它们混合在一起。

My code is as follows but it throws exception. If I remove UserDetailsServicerelated codes, the application runs and I can login in-memoryusers. As suggested below, I converted the configuration to XML based but users cannot sign-in.

我的代码如下,但它抛出异常。如果我删除UserDetailsService相关代码,应用程序就会运行并且我可以登录in-memory用户。如下所示,我将配置转换为基于 XML 的配置,但用户无法登录。

org.springframework.beans.factory.BeanCreationException: Error creating bean 
with name 'securityConfig': Injection of autowired dependencies failed; nested 
exception is org.springframework.beans.factory.BeanCreationException: Could 
not autowire field:  
org.springframework.security.core.userdetails.UserDetailsService 
com.myproj.config.SecurityConfig.userDetailsService; nested exception is 
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying 
bean of type    
[org.springframework.security.core.userdetails.UserDetailsService] found for 
dependency: expected at least 1 bean which qualifies as autowire candidate for 
this dependency. Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true),  
@org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}

Caused by: org.springframework.beans.factory.BeanCreationException: Could not 
autowire field 

org.springframework.security.core.userdetails.UserDetailsService 
com.myproj.config.SecurityConfig.userDetailsService; nested exception is 
org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 
[org.springframework.security.core.userdetails.UserDetailsService] 
found for dependency: expected at least 1 bean which qualifies as autowire 
candidate for this dependency. Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true), 
@org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 
[org.springframework.security.core.userdetails.UserDetailsService] found for 
dependency: expected at least 1 bean which qualifies as autowire candidate for 
this dependency. Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true), 
@org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}

Web.xml

网页.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_0.xsd"
    version="3.0">
    <listener>
        <listener-class>org.apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>proj</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
      <servlet-name>proj</servlet-name>
      <url-pattern>/</url-pattern>
    </servlet-mapping>



</web-app>

MvcWebApplicationInitializer

MvcWebApplicationInitializer

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;


public class MvcWebApplicationInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { SecurityConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

SecurityWebApplicationInitializer

SecurityWebApplicationInitializer

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {

}

SecurityConfig

安全配置

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(
                passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/resources/**", "/", "/index", "/aboutus")
                .permitAll()
                .antMatchers("/profile/**")
                .hasRole("USER")
                .and()
                .formLogin().loginPage("/signin").failureUrl("/signin?error")
                .permitAll().and().logout().logoutUrl("/signout").permitAll();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception        
    {
        return super.authenticationManagerBean();
    }

}

MemberServiceImpl

会员服务实现

@Service("userDetailsService")
public class MemberServiceImpl implements UserDetailsService {

    @Autowired
    MemberRepository memberRepository;

    private List<GrantedAuthority> buildUserAuthority(String role) {
        Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
        setAuths.add(new SimpleGrantedAuthority(role));
        List<GrantedAuthority> result = new ArrayList<GrantedAuthority>(
                setAuths);
        return result;
    }

    private User buildUserForAuthentication(Member member,
            List<GrantedAuthority> authorities) {
        return new User(member.getEmail(), member.getPassword(),
                member.isEnabled(), true, true, true, authorities);
    }

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        Member member = memberRepository.findByUserName(username);
        List<GrantedAuthority> authorities = buildUserAuthority("Role");
        return buildUserForAuthentication(member, authorities);
    }

}

Update 1

更新 1

Even after adding following annotation, and authenticationManagerBeanmethod from SecurityConfig the same exception is being thrown.

即使在添加以下注释和authenticationManagerBean来自 SecurityConfig 的方法之后,也会抛出相同的异常。

    @EnableGlobalMethodSecurity(prePostEnabled = true)

Update 2

更新 2

As suggested in one of the answers, I converted it to XML based configuration, the current code is as following;however, when I submit login form it does not do anything.

正如其中一个答案中所建议的那样,我将其转换为基于 XML 的配置,当前代码如下;但是,当我提交登录表单时,它什么也没做。

Spring-Security.xml

Spring-Security.xml

<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/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">



    <beans:import resource='login-service.xml' />
    <http auto-config="true" access-denied-page="/notFound.jsp"
        use-expressions="true">
        <intercept-url pattern="/" access="permitAll" />


        <form-login login-page="/signin" authentication-failure-url="/signin?error=1"
            default-target-url="/index" />
        <remember-me />
        <logout logout-success-url="/index.jsp" />
    </http>
    <authentication-manager>
        <authentication-provider>
            <!-- <user-service> <user name="admin" password="secret" authorities="ROLE_ADMIN"/> 
                <user name="user" password="secret" authorities="ROLE_USER"/> </user-service> -->
            <jdbc-user-service data-source-ref="dataSource"

                users-by-username-query="
              select username,password,enabled 
              from Member where username=?"

                authorities-by-username-query="
                      select username 
                      from Member where username = ?" />
        </authentication-provider>
    </authentication-manager>
</beans:beans>

login-service.xml

登录服务.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/testProject" />
    <property name="username" value="root" />
    <property name="password" value="" />
   </bean>

</beans>

回答by Charnjeet Singh

I think you forget for add this annotation on SecurityConfig Class

我认为您忘记在 SecurityConfig 类上添加此注释

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(
                passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/resources/**", "/", "/index", "/aboutus")
                .permitAll().antMatchers("/profile/**").hasRole("USER").and()
                .formLogin().loginPage("/signin").failureUrl("/signin?error")
                .permitAll().and().logout().logoutUrl("/signout").permitAll();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

and one things more I thinks this bean is not need

还有一件事我认为这个豆子是不需要的

 @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

Please try this hope this will work for you..

请试试这个希望这对你有用..

For get current user

获取当前用户

public String getUsername() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        if (authentication == null)
            return null;
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            return ((UserDetails) principal).getUsername();
        } else {
            return principal.toString();
        }
    }


    public User getCurrentUser() {
        if (overridenCurrentUser != null) {
            return overridenCurrentUser;
        }
        User user = userRepository.findByUsername(getUsername());

        if (user == null)
            return user;
    }

Thanks

谢谢

回答by The Student Soul

I think the issue could be due to missing @ComponentScanannotation. When trying to autowire userDetailsServicein SecurityConfig, it's not able to find a suitable bean to autowire with.

我认为这个问题可能是由于缺少@ComponentScan注释。尝试在 中自动装配userDetailsServiceSecurityConfig,无法找到合适的 bean 进行自动装配。

A spring application usually has a separate "application context", in addition to "mvc context", "security context" (which you already have via SecurityConfig), etc.

除了“mvc 上下文”、“安全上下文”(您已经通过SecurityConfig)等之外,spring 应用程序通常具有单独的“应用程序上下文” 。

I'm not sure if putting @ComponentScanon SecurityConfigitself will work on not, but you can give it a try:

我不确定穿上@ComponentScanSecurityConfig自己是否会起作用,但您可以尝试一下:

@Configuration
@ComponentScan("your_base_package_name_here")
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}

Replace "your_base_package_name_here" with the name of the package containing your @Componentor @Serviceclasses.

将“your_base_package_name_here”替换为包含您的@Component@Service类的包的名称。

If this doesn't work, add a new, empty class with @ComponentScanannotation:

如果这不起作用,请添加一个带有@ComponentScan注释的新的空类:

@Configuration
@ComponentScan("your_base_package_name_here")
public class AppConfig {
    // Blank
}

Source: http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch06s02.html

来源:http: //docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch06s02.html

回答by Pramod Gaikwad

Spring cannot find the bean with qualifier userDetailsService. I think you should check your applicationContext.xmlfile in case if you have forgot to configure UserDetailsService's bean for Spring Security.If it there then try once by removing @Qualifier("userDetailsService").

Spring 找不到带有 qualifier 的 bean userDetailsServiceapplicationContext.xml如果您忘记UserDetailsService为 Spring Security配置bean,我认为您应该检查您的文件。如果存在,则通过删除@Qualifier("userDetailsService").

follow this link. context.xml file configured against spring security

按照这个链接。 针对 spring 安全性配置的 context.xml 文件

回答by Jorge Jiménez Barra

Try changing the field type:

尝试更改字段类型:

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    MemberServiceImpl userDetailsService;

回答by MS Ibrahim

See there are some errors exist in your code base try to resolve it by seeing the code below.

看到您的代码库中存在一些错误,请尝试通过查看下面的代码来解决它。

Remove your SecurityConfig file and convert into xml file based configuration.

删除您的 SecurityConfig 文件并转换为基于 xml 文件的配置。

Your spring-security.xml should look like this.

您的 spring-security.xml 应如下所示。

   <security:http auto-config="true" >  
  <security:intercept-url pattern="/index*" access="ROLE_USER" />  
  <security:form-login login-page="/login" default-target-url="/index"  
   authentication-failure-url="/fail2login" />  
  <security:logout logout-success-url="/logout" />  
 </security:http>  

    <security:authentication-manager>  
   <security:authentication-provider>  
     <!-- <security:user-service>  
   <security:user name="samplename" password="sweety" authorities="ROLE_USER" />  
     </security:user-service> -->  
     <security:jdbc-user-service data-source-ref="dataSource"    
      users-by-username-query="select username, password, active from users where username=?"   
          authorities-by-username-query="select us.username, ur.authority from users us, user_roles ur   
        where us.user_id = ur.user_id and us.username =?  "   
  />  
   </security:authentication-provider>  
 </security:authentication-manager>  

web.xml should be looking like this:

web.xml 应该是这样的:

 <servlet>  
  <servlet-name>sdnext</servlet-name>  
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <load-on-startup>1</load-on-startup>  
 </servlet>  

 <servlet-mapping>  
  <servlet-name>sdnext</servlet-name>  
  <url-pattern>/</url-pattern>  
 </servlet-mapping>  
 <listener>  
  <listener-class>  
                  org.springframework.web.context.ContextLoaderListener  
        </listener-class>  
 </listener>  

 <context-param>  
  <param-name>contextConfigLocation</param-name>  
  <param-value>  
   /WEB-INF/sdnext-*.xml,  
  </param-value>  
 </context-param>  

 <welcome-file-list>  
  <welcome-file>index</welcome-file>  
 </welcome-file-list>  

 <!-- Spring Security -->  
 <filter>  
  <filter-name>springSecurityFilterChain</filter-name>  
  <filter-class>  
                  org.springframework.web.filter.DelegatingFilterProxy  
                </filter-class>  
 </filter>  

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

回答by sanastasiadis

It seams that your "userDetailsService" bean is declared @Autowired, but it's not available as a class (MemberServiceImpl) in the context of your SecurityConfig.

看来您的“userDetailsS​​ervice”bean 被声明为@Autowired,但它在您的SecurityConfig上下文中不能作为类(MemberServiceImpl)使用。

I suppose in your MvcWebApplicationInitializeryou should include MemberServiceImplalso like:

我想在你的MvcWebApplicationInitializer你应该包括MemberServiceImpl也喜欢:

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[] { SecurityConfig.class, MemberServiceImpl.class };
}

回答by Phash

I am using Wicket and ran into the same problem. I could solve this problem by changeing the order in my AppInit Class to scan the package first, then register the calling Bean

我正在使用 Wicket 并遇到了同样的问题。我可以通过改变我的 AppInit 类中的顺序来解决这个问题,先扫描包,然后注册调用 Bean

public class AppInit implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException 
{

        // Create webapp context
        AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
        root.scan("my_package");
        root.register(SpringSecurityConfiguration.class);
...#
}

回答by fjmodi

Try adding the following method to your SecurityConfig:

尝试将以下方法添加到您的 SecurityConfig:

@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
    return super.userDetailsServiceBean();
}

回答by Pavan

Here is the answer , using the proper @ComponentScan will resolve, below is sample code snippet I am pasting which I also faced the same problem and resolved. Below is solved and works for issue related to bean creation exception for org.springframework.security.core.userdetails.UserDetailsService

这是答案,使用正确的@ComponentScan 将解决,下面是我粘贴的示例代码片段,我也遇到了同样的问题并已解决。以下已解决并适用于与org.springframework.security.core.userdetails.UserDetailsS​​ervice 的bean 创建异常相关的问题



Step1:Write the Security Application Configuration class

Step1:编写安全应用配置类

import org.springframework.security.core.userdetails.UserDetailsService;

    @Configuration
    @EnableWebSecurity
    public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        @Qualifier("userDetailsServiceImpl")
        UserDetailsService userDetailsService;

Here @ComponentScan is not mandatory in LoginSecurityConfig, you can define @ComponentScan in the root config class like below and import the LoginSecurityConfig.classLoginSecurityConfig.

这里 @ComponentScan 在LoginSecurityConfig 中不是强制性的 ,您可以像下面这样在根配置类中定义 @ComponentScan 并导入LoginSecurityConfig.classLoginSecurityConfig。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example" })
@Import(value = { LoginSecurityConfig.class })
public class LoginApplicationConfig 

Step2:Now Autowiring the SpringBean org.springframework.security.core.userdetails.UserDetailsService

步骤 2:现在自动装配 SpringBean org.springframework.security.core.userdetails.UserDetailsS​​ervice

@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements org.springframework.security.core.userdetails.UserDetailsService {

@Autowired
UserDao userDao;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

User user = userDao.findByUsername(username);

if (user == null) {
            System.out.println("User not found");
            throw new UsernameNotFoundException("Username not found");
}


  return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), true, true, true, true, getGrantedAuthorities(user));
}

private List<GrantedAuthority> getGrantedAuthorities(User user) {
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

    authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    return authorities;
}

    }//End of Class