【问题标题】:Access messages.properties in Spring Security configuration在 Spring Security 配置中访问 messages.properties
【发布时间】:2016-03-21 01:04:21
【问题描述】:

我最近切换到 Spring Security 来处理我的 Web 应用程序的登录、注销和基于角色的访问。我使用闪存消息来获取信息和错误消息,并希望在成功登录和注销后显示这样的消息。

在 JSP 文件中,这些消息使用以下代码显示:

<c:if test = "${not empty flashmessage_error}" >
  <div class="alert alert-danger"><c:out value="${flashmessage_error}" />
  </div>
</c:if>

<c:if test="${not empty flashmessage_info}" >
  <div class="alert alert-info"><c:out value="${flashmessage_info}" />
  </div>
</c:if>

为了在成功登录后显示一个 flash 消息,我定义了一个 custom LoginSuccessHandler,它将消息放入 FlashMap 并将重定向页面设置为主页:

public class FlashMessageAuthSuccessHandler extends
    SavedRequestAwareAuthenticationSuccessHandler {

private String flashMessage = "Logged in!";

public void setFlashMessage(String flashMessage) {
      this.flashMessage = flashMessage;
}

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
        HttpServletResponse response, Authentication authentication)
        throws ServletException, IOException {

    FlashMapManager flashMapManager = new SessionFlashMapManager();

    FlashMap fm = new FlashMap();
    fm.put(KeyConstants.FLASH_INFO_KEY, flashMessage);

    flashMapManager.saveOutputFlashMap(fm, request, response);

    setDefaultTargetUrl("/");

    super.onAuthenticationSuccess(request, response, authentication);
}
}

我的 SecurityConfig 类如下所示:

@Configuration
@EnableWebSecurity(debug = false)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService userDetailsService;

@Autowired
private MessageSource messageSource;


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    auth.authenticationProvider(daoAuthenticationProvider());

}

/*
 * HttpSecurity allows configuring web based security for specific HTTP requests
 * By default it will be applies to all request, but can be restricted
 * using requestMatcher
 * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
 */

@Override
protected void configure(HttpSecurity http) throws Exception {
    //super.configure(http);

    http
        .authorizeRequests()
            .antMatchers("/", "/css/**", "/js/**", "/images/**", "/about", "/error").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .usernameParameter("j_username")
            .passwordParameter("j_password")
            .loginPage("/login")
            .loginProcessingUrl("/ssprocesslogin") //post request
//          .defaultSuccessUrl("/") //cannot possibly used in combination with successHandler?
            .successHandler(loginSuccesHandler())
            .failureUrl("/login?" + KeyConstants.FLASH_ERROR_KEY + "=Verkeerde login message aanpassen") //default is /login?error
            .permitAll()
            .and()
        .rememberMe()
            .tokenValiditySeconds(2592000)  //one month
            .rememberMeParameter("j_rememberme")
            .key("wIllekEUrigeSleutteL")
            .userDetailsService(userDetailsService)
        .and()
        .logout()
            .logoutUrl("/logout") //default is /logout
//          .logoutSuccessUrl("/") //default is /login?logout
            .logoutSuccessHandler(new FlashMessageLogoutSuccessHandler())
            .invalidateHttpSession(true) //true is the default
            ; 
}

@Bean
public FlashMessageAuthSuccessHandler loginSuccesHandler() {
    FlashMessageAuthSuccessHandler handler = new FlashMessageAuthSuccessHandler();

    //THIS DOES NOT WORK!!!!!
    String loginSuccessMessage = messageSource.getMessage("flashmessage.loginsuccesful", null, "not found", null);

    handler.setFlashMessage(loginSuccessMessage);
    return handler;
}

@Bean
public AuthenticationProvider daoAuthenticationProvider() {
    DaoAuthenticationProvider dap = new DaoAuthenticationProvider();
    dap.setUserDetailsService(userDetailsService);
    dap.setPasswordEncoder(passwordEncoder());

    return dap;
}

@Bean
public ShaPasswordEncoder passwordEncoder() {
    return new ShaPasswordEncoder(256);
}
}

但没有显示来自 messages.properties 文件的正确消息。相反,字符串“未找到”在登录后显示为闪存消息。 在下面的行中,我尝试使用注入的 MessageSource 通过消息键查找消息,但显然没有找到,并且显示了回退消息:

String loginSuccessMessage = messageSource.getMessage("flashmessage.loginsuccesful", null, "not found", null);

messageSource bean 在 MvcConfig 文件中定义,如下所示,当用于在 JSP 文件中显示消息时工作正常,但它似乎无法在 Spring Security 配置文件中自动装配以获得所需的 Flash 消息。

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

有什么想法可以做到这一点吗?

【问题讨论】:

    标签: spring-mvc spring-security


    【解决方案1】:

    在 Spring Boot 2.3 中,spring-boot-starter-thymeleaf 依赖项对我有用:

    在我的@Configuration 班级:

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.US);
        return slr;
    }
    
    @Bean
    public MessageSourceAccessor getMessageSourceAccessor(MessageSource messageSource) {
        return new MessageSourceAccessor(messageSource);
    }
    

    然后在任何我想从 messages.properties 动态访问值的 bean 中:

    @Autowired
    MessageSourceAccessor messageSourceAccessor;
    ...
    messageSourceAccessor.getMessage(key)
    

    【讨论】:

      【解决方案2】:

      您在配置对象中自动装配消息源。 MessageSource bean 很可能还没有准备好。

      我会尝试这样的:

      public class FlashMessageAuthSuccessHandler extends
          SavedRequestAwareAuthenticationSuccessHandler {
      
      @Value(${flashmessage.loginsuccesful})
      private String flashMessage;
      
      public void setFlashMessage(String flashMessage) {
            this.flashMessage = flashMessage;
      }
      
      @Override
      public void onAuthenticationSuccess(HttpServletRequest request,
              HttpServletResponse response, Authentication authentication)
              throws ServletException, IOException {
      
          FlashMapManager flashMapManager = new SessionFlashMapManager();
      
          FlashMap fm = new FlashMap();
          fm.put(KeyConstants.FLASH_INFO_KEY, flashMessage);
      
          flashMapManager.saveOutputFlashMap(fm, request, response);
      
          setDefaultTargetUrl("/");
      
          super.onAuthenticationSuccess(request, response, authentication);
      }
      }
      

      【讨论】:

        【解决方案3】:

        在 applicationContext.xml 中,添加以下内容:

        <util:properties id="message" location="classpath:com/your/program/resources/message.properties" />
        

        如果使用基于 java 的配置,则如下:

        @Bean(name = "message")
        public PropertiesFactoryBean message() {
            PropertiesFactoryBean bean = new PropertiesFactoryBean();
            bean.setLocation(new ClassPathResource("com/your/program/resources/message.properties"));
            return bean;
        }
        

        创建一个字段变量并在安全配置中使用@Value注解,如下所示:

        @Value(value = "#{'${flashmessage.loginsuccesful}'}")
        private String loginSuccessMessage;
        

        @Value("#{message['flashmessage.loginsuccesful']}")
        private String loginSuccessMessage;
        

        查看link 以供参考。

        【讨论】:

          【解决方案4】:

          我认为这确实是MessageSource没有准备好注入Configuration对象的问题。您的解决方案可能会起作用,并且涉及使用 @Value 注释从 application.properties 文件中读取属性。我已经在我的 AppConfig 类中阅读了这个文件:

          @Configuration
          @PropertySource(value= {"classpath:application.properties"})
          public class AppConfig {
          }
          

          并在注入 Environment 对象的其他配置文件之一中访问所需的值:

          @Autowired
          private Environment env;
          

          在 loginSuccesHandler() 方法中:

          String loginSuccessMessage = env.getProperty("flashmessage.loginsuccesful");
          

          这确实有效并部分解决了问题。剩下的问题是属性声明:

          flashmessage.loginsuccesful=You have been logged in succesfully!
          

          需要从messages.properties 移动到application.properties,而我使用属性的主要原因是将其与其他本地化消息一起定义,将来可能会使用不同的语言。 但也许这根本不可能,因为此时不知道语言环境?

          更新: 我通过将 MessageSource bean 的声明从 Dispatcher servlet 上下文 (MvcConfig.java) 移动到 Root 上下文 (AppConfig.java) 解决了这个问题。 SecurityConfig 类显然无法访问 servlet 上下文。

          【讨论】:

            猜你喜欢
            • 2017-08-13
            • 1970-01-01
            • 2017-07-09
            • 2016-09-14
            • 1970-01-01
            • 1970-01-01
            • 2018-02-02
            • 2016-12-17
            • 1970-01-01
            相关资源
            最近更新 更多