【问题标题】:How to convert the spring security xml configuration hibernate into java config using Spring-Security 3 and Hibernate 4如何使用 Spring-Security 3 和 Hibernate 4 将 spring security xml 配置 hibernate 转换为 java config
【发布时间】:2014-04-06 06:25:42
【问题描述】:

我刚刚了解了 Spring Security,并想使用 java hibernate 配置连接到数据库,但我发现很少有示例或教程。通过使用 xml 配置,我发现了更多信息。我在这里使用 Spring 4.0.2、Spring-Security 3.2.0 和 Hibernate 4.3.2

我的问题是: 如何将下面的xml转换成java配置?

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

CustomUserDetailsS​​ervice.java 所在

package com.whatever.svtest.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.whatever.svtest.dao.UserDao;

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

    @Autowired
    private UserDao userDao;

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

        com.whatever.svtest.model.User domainUser = userDao.getByUsername(username);

        if (domainUser == null) {
            throw new UsernameNotFoundException("user not found");
        }

        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority("USER"));        

        return new User(username, domainUser.getPassword(), true, true, true, true, authorities);
    }

}

在 SecurityConfig.java 上,我使用 spring 创建的默认登录表单。我正在尝试自己弄清楚如何将 xml 配置转换为 java 配置。

package com.whatever.svtest.init;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

import com.whatever.svtest.service.impl.UserServiceImpl;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(new UserServiceImpl()).passwordEncoder(NoOpPasswordEncoder.getInstance());

    }

}

我把 SecurityConfiguration.java 像这样放在 Initializer.java 上

package com.whatever.svtest.init;

import javax.servlet.Filter;

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

public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

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

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

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] { new DelegatingFilterProxy("springSecurityFilterChain") };
    }

}

WebAppConfig.java

package com.whatever.svtest.init;

import javax.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@Import({ DatabaseConfig.class })
@ComponentScan(basePackages = { "com.whatever.svtest.controller" })
@PropertySource({ "classpath:persistence-mysql.properties" })
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Resource   
    private Environment env;

    @Override   
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
    }

    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasename("messages");
        source.setUseCodeAsDefaultMessage(true);
        return source;
    }

    @Bean
    public ViewResolver setupViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

}

当我运行我的网络应用程序时,我得到了这个。 (我把图片放在这里http://i.stack.imgur.com/Mssrc.jpg

我还(某处)阅读了有关创建 AuthenticationProvider.java 的自定义实现的信息,但我不知道将这段代码放在哪里..

package com.whatever.svtest.init;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import com.whatever.svtest.dao.UserDao;
import com.whatever.svtest.model.User;

public class MyAuthProvider implements AuthenticationProvider {

    @Autowired
    private UserDao userDao;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
        User user = userDao.getByUsername(name);
        authentication.setAuthenticated(user != null && password.equals(user.getPassword()));
        return authentication;
    }

    @Override
    public boolean supports(Class<?> authentication) {

        return (MyAuthProvider.class.isAssignableFrom(authentication));
    }

}

【问题讨论】:

  • 我能知道您为什么特别寻找注解来配置 Spring Security 吗?至少对于 Spring Security 而言,将其作为 xml 配置具有更大的优势,这使得在不触及现有代码的情况下可以灵活地更改它。您可以在此链接中获得一些编程配置stackoverflow.com/questions/19353578/…
  • 嗨,杰伊,我没有理由“为什么”。我是spring框架的新人。我刚从 3 周前开始学习 Spring Framework。最近我看到了很多变化。一切都从 xml 转换为 java 配置。所以,我为什么不学习最新的.. :)

标签: java hibernate login configuration spring-security


【解决方案1】:

[已解决]

在为自己的代码苦苦挣扎两天后,我终于找到了解决方案..!

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = SecurityConfiguration.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

}

我不必创建新 bean。我只需要将 UserService 对象传递给 userDetailsS​​ervice 方法,放入 autowired,当然使用 @ComponentScan 到当前类UserService 类已经有一个 UserDao,我在其中实现了 UserDetailsS​​ervice

@Service("userService")
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService, UserDetailsService {

    @Autowired
    private UserDao userDao;

    // other method

    @Override
    public User getByUsername(String username) {
        return userDao.getByUsername(username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = getByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("user not found");
        } else {
            List<GrantedAuthority> listAuthorities = new ArrayList<GrantedAuthority>();
            listAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            return new org.springframework.security.core.userdetails.User(username, user.getPassword(), true, true, true, true, listAuthorities);
        }

    }
}

感谢 Rob Winch 提供线索。

【讨论】:

    【解决方案2】:

    配置不一致?

    您发布的配置对我来说不太有意义。具体如下:

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new UserServiceImpl()).passwordEncoder(NoOpPasswordEncoder.getInstance());
    }
    

    解决方案

    您似乎没有定义 UserServiceImpl,但是您已经定义了 CustomUserDetailsS​​ervice(这可能是应该传入的参数。但是,为了使 bean 自动装配,您需要将其创建为 bean。所以你应该改变你的配置:

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(uds());
    }
    
    @Bean
    public CustomUserDetailsService uds() {
        return new CustomUserDetailsService();
    }
    

    通过将 CustomUserDetailsS​​ervice 作为 @Bean 返回,您可以确保 Spring 正确地自动装配它。

    一些补充说明:

    • 您不需要自定义 AuthenticationProvider。这是因为您使用用户名/密码进行身份验证,所以 UserDetailsS​​ervice 很好。如果您想使用用户名/密码以外的其他内容进行身份验证,您将创建一个自定义 AuthenticationProvider
    • 无需指定无操作密码编码器,因为这是默认设置。

    改进 CustomUserDetailsS​​ervice

    在您当前的实现中需要指出的一点是,虽然您可以直接@Autowire 字段,但它更容易出错,因此您可能应该更改您的 CustomUserDetailsS​​ervice 以拥有一个允许注入 UserDao 的构造函数。这也使单元测试更容易(因此您不需要使用反射来设置 UserDao)。因此,您会将 CustomUserDetailsS​​ervice 更新为:

    @Service
    @Transactional(readOnly = true)
    public class CustomUserDetailsService implements UserDetailsService {
    
        private UserDao userDao;
    
        @Autowired
        public CustomUserDetailsService(UserDao userDao) {
            this.userDao = userDao;
        }
    

    那么你的配置可以如下:

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(uds());
    }
    
    @Autowired
    private UserDao userDao;
    
    @Bean
    public CustomUserDetailsService uds() {
        return new CustomUserDetailsService(userDao);
    }
    

    根据新错误更新

    您还需要确保将您的 UserDao 作为 Bean 拾取。例如:

    @Bean
    public UserDao userDao() {
        return new UserDao(...);
    }
    

    注意:确保正确初始化 UserDao(即确保其所有依赖项都已初始化。如果您在 UserDao 上使用 Autowired,请确保这些依赖项也是 @Bean

    【讨论】:

    • 感谢您的回答。我听从你的指示。但它仍然无法正常工作。我收到错误 pastebin.com/htHBH5RN
    • 我发现这个链接 (stackoverflow.com/questions/8162698/…) 虽然它不使用休眠,但它解决了我的问题,但正如你所说的“我不需要自定义”AuthenticationProvider,它让我更加困惑。跨度>
    • 您的 UserDao 也需要定义为 Bean。查看我的更新
    猜你喜欢
    • 2018-01-31
    • 2012-01-23
    • 2012-12-05
    • 2015-07-22
    • 2020-08-06
    • 2015-10-08
    • 2016-09-01
    • 2012-12-04
    • 2015-08-12
    相关资源
    最近更新 更多