【问题标题】:Spring Security with UserDetailsService implementation具有 UserDetailsS​​ervice 实现的 Spring Security
【发布时间】:2018-10-25 19:10:14
【问题描述】:

我一直在尝试使用 UserDetailsS​​ervice 设置 Spring Security。例如,我使用了baeldung's tutorial。该应用程序已启动,没有任何异常,但身份验证不起作用。 目前,我为每个应用模块都有一个核心 spring java config 和 spring java config。

核心 Spring Java 配置:

public class AppInitializer implements WebApplicationInitializer {

@Override
@Autowired
public void onStartup(ServletContext container) throws ServletException {
    AnnotationConfigWebApplicationContext context = getContext();
    container.addListener(new ContextLoaderListener(context));

    container.setInitParameter("spring.profiles.active", "dev"); //Workaround for NamingException
    container.setInitParameter("spring.profiles.default", "dev"); //Workaround for NamingException
    container.setInitParameter("spring.liveBeansView.mbeanDomain", "dev"); //Workaround for NamingException

    ServletRegistration.Dynamic mainDispatcher =
            container.addServlet("dispatcher", new DispatcherServlet(context));
    ServletRegistration.Dynamic businessDispatcher =
            container.addServlet("businessDispatcher", BusinessAppConfig.createDispatcherServlet(context));
    ServletRegistration.Dynamic ppaDispatcher =
            container.addServlet("ppaDispatcher", PpaAppConfig.createDispatcherServlet(context));

    initDispatcher(mainDispatcher, 1, "/");
    initDispatcher(businessDispatcher, 2, "/business");
    initDispatcher(businessDispatcher, 3, "/ppa");
}

private void initDispatcher(ServletRegistration.Dynamic dispatcher, int loadOnStartUp, String mapping) {
    if (dispatcher == null) {
        System.out.println("Servlet" + dispatcher.getName() + " is already added");
    } else {
        dispatcher.setLoadOnStartup(loadOnStartUp);
        dispatcher.addMapping(mapping);
    }
}

public AnnotationConfigWebApplicationContext getContext() {
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(MvcConfiguration.class);
    return context;
}

@Bean(name = "propertyConfigurer")
public PropertySourcesPlaceholderConfigurer getPropertyPlaceholderConfigurer() {
    PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    placeholderConfigurer.setLocation(new ClassPathResource("common.properties"));
    placeholderConfigurer.setLocation(new ClassPathResource("amazon.S3Storage.properties"));
    placeholderConfigurer.setLocation(new ClassPathResource("local.storage.properties"));
    placeholderConfigurer.setLocation(new ClassPathResource("log4j.properties"));
    placeholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
    return placeholderConfigurer;
}

}

业务模块的 Spring java conf

@Configuration
public class BusinessAppConfig {

    public static Servlet createDispatcherServlet(AnnotationConfigWebApplicationContext context) {
        context.register(BusinessMvcConfig.class);
        context.register(BusinessHibernateConfig.class);
        context.register(BusinessSecurityConfig.class);
        return new DispatcherServlet(context);
    }
}

业务模块的 Spring Security java conf

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class BusinessSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

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

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

    @Autowired
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(getPasswordEncoder());
        return authProvider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().
                and().formLogin().permitAll().
                and().logout().permitAll();
    }

    @Bean(name = "passwordEncoder")
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder(11);
    }
}

UserDetailsS​​ervice 实现

@Service
public class UserDetailsServiceImpl extends BaseServiceImpl<User, UserRepository<User>> implements UserDetailsService, UserService {

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

        User user = dao.findUserByLogin(login);

        if (user == null) {
            throw new UsernameNotFoundException(login);
        }

        return new UserPrincipal(user);
    }
}

基于用户模型的UserDetails

public class UserPrincipal implements UserDetails, Serializable {

    private User user;

    public UserPrincipal(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return user.isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return user.isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return user.isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return user.isEnabled();
    }
}

在调试模式下,我遇到了几个异常

2018-05-16 22:23:51 DEBUG InjectionMetadata:74 - Registered injected element on class [business.config.BusinessSecurityConfig$$EnhancerBySpringCGLIB$$c0bd9f7f]: AutowiredMethodElement for public org.springframework.security.authentication.dao.DaoAuthenticationProvider business.config.BusinessSecurityConfig.authenticationProvider()
2018-05-16 22:23:51 DEBUG InjectionMetadata:74 - Registered injected element on class [business.config.BusinessSecurityConfig$$EnhancerBySpringCGLIB$$c0bd9f7f]: AutowiredMethodElement for public void business.config.BusinessSecurityConfig.configureGlobal(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder) throws java.lang.Exception
2018-05-16 22:23:51 DEBUG DefaultListableBeanFactory:569 - Eagerly caching bean 'businessSecurityConfig' to allow for resolving potential circular references

[org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$$EnhancerBySpringCGLIB$$3d61bda9]: AutowiredMethodElement for public void org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.setGlobalAuthenticationConfigurers(java.util.List) throws java.lang.Exception

2018-05-16 22:24:11 DEBUG AnnotationUtils:1889 - Failed to meta-introspect annotation interface org.springframework.beans.factory.annotation.Autowired: java.lang.NullPointerException

那么,这个配置有什么问题呢?

【问题讨论】:

  • 您应该包含一些日志以查看实际发生的情况,只是说明它不起作用很难找到原因
  • 我打开了调试模式,遇到了几个异常。我将它们附加到主题的底部
  • 如果有帮助,最新的更改已提交给github.com/BessonovEvgeniy/Octava/tree/spring-security-support 分支

标签: java spring spring-security


【解决方案1】:

所以,我在配置 Spring Security 时犯了几个错误。 发现这些错误有助于分析调试消息。

更改前请求的调试消息:

2018-06-03 00:14:26 DEBUG DispatcherServlet:891 - DispatcherServlet with name 'dispatcher' processing GET request for [/]
2018-06-03 00:14:26 DEBUG RequestMappingHandlerMapping:312 - Looking up handler method for path /
2018-06-03 00:14:26 DEBUG RequestMappingHandlerMapping:319 - Returning handler method [public java.lang.String business.controller.HomePageController.getHomePage(java.util.Map<java.lang.String, java.lang.Object>)]
2018-06-03 00:14:26 DEBUG DefaultListableBeanFactory:254 - Returning cached instance of singleton bean 'homePageController'
2018-06-03 00:14:26 DEBUG DispatcherServlet:979 - Last-Modified value for [/] is: -1
2018-06-03 00:14:26 DEBUG DispatcherServlet:1319 - Rendering view [org.springframework.web.servlet.view.JstlView: name 'index'; URL [/WEB-INF/views/index.jsp]] in DispatcherServlet with name 'dispatcher'
2018-06-03 00:14:26 DEBUG DefaultListableBeanFactory:254 - Returning cached instance of singleton bean 'requestDataValueProcessor'
2018-06-03 00:14:26 DEBUG JstlView:168 - Forwarding to resource [/WEB-INF/views/index.jsp] in InternalResourceView 'index'
2018-06-03 00:14:26 DEBUG DispatcherServlet:1000 - Successfully completed request
2018-06-03 00:14:31 DEBUG DispatcherServlet:891 - DispatcherServlet with name 'dispatcher' processing GET request for [/business/project/new]
2018-06-03 00:14:31 DEBUG RequestMappingHandlerMapping:312 - Looking up handler method for path /business/project/new
2018-06-03 00:14:31 DEBUG RequestMappingHandlerMapping:319 - Returning handler method [public java.lang.String business.controller.ProjectController.newProject(org.springframework.ui.Model) throws java.lang.Exception]
2018-06-03 00:14:31 DEBUG DefaultListableBeanFactory:254 - Returning cached instance of singleton bean 'projectController'
2018-06-03 00:14:31 DEBUG DispatcherServlet:979 - Last-Modified value for [/business/project/new] is: -1

在这里我们可以看到两个请求。

  • 第一个请求 "/" 由 mainDispatcher 处理(请参阅 AppInitializer 一开始)。
  • 第二个请求 /business/project/newmainDispatcher 也是如此,但 businessDispatcher 处理程序是预期的。

问题是路径错误。要解决此问题,您应该更改旧设置:

ServletRegistration.Dynamic businessDispatcher =
    container.addServlet("businessDispatcher", BusinessAppConfig.createDispatcherServlet(context));
initDispatcher(businessDispatcher, 2, "/business/*");

路径改变后调试:

2018-06-03 20:33:43 DEBUG DispatcherServlet:891 - DispatcherServlet with name 'businessDispatcher' processing GET request for [/business/]
2018-06-03 20:33:43 DEBUG RequestMappingHandlerMapping:312 - Looking up handler method for path /
2018-06-03 20:33:43 DEBUG RequestMappingHandlerMapping:319 - Returning handler method [public java.lang.String business.controller.HomePageController.getHomePage(java.util.Map<java.lang.String, java.lang.Object>)]
2018-06-03 20:33:43 DEBUG DefaultListableBeanFactory:254 - Returning cached instance of singleton bean 'homePageController'
2018-06-03 20:33:43 DEBUG DispatcherServlet:979 - Last-Modified value for [/business/] is: -1
2018-06-03 20:33:43 DEBUG DispatcherServlet:1319 - Rendering view [org.springframework.web.servlet.view.JstlView: name 'index'; URL [/WEB-INF/views/index.jsp]] in DispatcherServlet with name 'businessDispatcher'
2018-06-03 20:33:43 DEBUG DefaultListableBeanFactory:254 - Returning cached instance of singleton bean 'requestDataValueProcessor'
2018-06-03 20:33:43 DEBUG JstlView:168 - Forwarding to resource [/WEB-INF/views/index.jsp] in InternalResourceView 'index'
2018-06-03 20:33:43 DEBUG DispatcherServlet:1000 - Successfully completed request

因此,您可以在此处看到 /business/project/ URL 由正确的调度程序 - businessDispatcher 处理。

另外,控制器@ReqestMapping 应该改变 来自

@Controller
@RequestMapping(value = "/business/project")
public class ProjectController {

@Controller
@RequestMapping(value = "/project")
public class ProjectController {

为了保护方法免受浏览器地址行中直接键入 URL 的影响,需要进行以下更改:

检查 安全启用=真, prePostEnabled = true

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class BusinessSecurityConfig extends WebSecurityConfigurerAdapter {

每个控制器或特定方法都应由 @PreAuthorize

注释
@Controller
@RequestMapping(value = "/project")
@PreAuthorize("isAuthenticated()")
public class ProjectController {

@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/new", method = RequestMethod.GET)
public String newProject (Model model) throws Exception {
    model.addAttribute(new Project());
    return "project/new";
}

注意如果未经授权的用户试图访问受保护的方法 AuthenticationCredentialsNotFoundException 将被传播。

要处理此类异常,请参阅here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-27
    • 2013-05-12
    • 2012-12-25
    • 2012-06-11
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 2016-03-09
    相关资源
    最近更新 更多