如何使用 Spring-Security 3 和 Hibernate 4 将 spring security xml 配置 hibernate 转换为 java config

Posted

技术标签:

【中文标题】如何使用 Spring-Security 3 和 Hibernate 4 将 spring security xml 配置 hibernate 转换为 java config【英文标题】:How to convert the spring security xml configuration hibernate into java config using Spring-Security 3 and Hibernate 4 【发布时间】: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/Ms-s-rc.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 配置具有更大的优势,这使得在不触及现有代码的情况下可以灵活地更改它。您可以在此链接中获得一些编程配置***.com/questions/19353578/… 嗨,杰伊,我没有理由“为什么”。我是spring框架的新人。我刚从 3 周前开始学习 Spring Framework。最近我看到了很多变化。一切都从 xml 转换为 java 配置。所以,我为什么不学习最新的.. :) 【参考方案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 我发现这个链接 (***.com/questions/8162698/…) 虽然它不使用休眠,但它解决了我的问题,但正如你所说的“我不需要自定义”AuthenticationProvider,它让我更加困惑。跨度> 您的 UserDao 也需要定义为 Bean。查看我的更新

以上是关于如何使用 Spring-Security 3 和 Hibernate 4 将 spring security xml 配置 hibernate 转换为 java config的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 spring-security 和 jQuery 处理过期会话?

spring-security 收到空凭据(用户名和密码)时如何创建自定义响应?

如何使用 Spring-Boot 播种 Spring-Security

如何使用我自己的机制自定义 spring-security 身份验证过程

有啥方法可以强制在 spring-security 中的某些页面使用 https 吗?

如何使用 spring-security 设置 JerseyTest?