web-dev-qa-db-ja.com

Thymeleafを使用したSpring Securityの簡単な例

こんにちは、私はこのページで見つけた簡単なログインフォームページを行うことについての簡単な例に従うことを試みています_http://docs.spring.io/autorepo/docs/spring-security/4.0.x/guides/form.html_

問題は、ログインしようとするたびにこのエラーが発生することです。_Expected CSRF token not found. Has your session expired?_

このエラーが発生したら、エクスプローラーで[戻る]ボタンを押して、もう一度ログインしてみてください。そうすると、このエラーが表示されます:_HTTP 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'_

チュートリアルページには、このメッセージがあります:_We use Thymeleaf to automatically add the CSRF token to our form. If we were not using Thymleaf or Spring MVCs taglib we could also manually add the CSRF token using <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>_

「したがって、thymeleafも使用しているため、そのタグをページに追加しませんでした」

私は別のソリューションを見つけ、それが機能し、このソリューションはこれをセキュリティ設定クラスに追加しています.csrf().disable()このソリューションは機能しますが、これは私のページでcsrf保護を無効にすることであり、これを無効にしたくないと思います保護のタイプ。

これは私のsecurity-configクラスです:

_@Configuration
@EnableWebSecurity
public class ConfigSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }


    @Override
    protected void configure( HttpSecurity http ) throws Exception {
        http

        //.csrf().disable() is commented because i dont want disable this kind of protection 
        .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()                                    
                .permitAll();
    }
}
_

私のセキュリティイニシャル:

_public class InitSecurity extends AbstractSecurityWebApplicationInitializer {

    public InicializarSecurity() {
        super(ConfigSecurity .class);

    }
}
_

私のthymeleaf設定があるapp-configクラス

_@EnableWebMvc
@ComponentScan(basePackages = {"com.myApp.R10"})
@Configuration
public class ConfigApp extends WebMvcConfigurerAdapter{

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/css/**").addResourceLocations("/css/**");
        registry.addResourceHandler("/img/**").addResourceLocations("/img/**");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/**");
        registry.addResourceHandler("/sound/**").addResourceLocations("/sound/**");
        registry.addResourceHandler("/fonts/**").addResourceLocations("/fonts/**");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
      public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new       ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages/messages");
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(0);// # -1 : never reload, 0 always reload
        return messageSource;
    }
//  THYMELEAF

        @Bean 
        public ServletContextTemplateResolver templateResolver() {
            ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
            resolver.setPrefix("/WEB-INF/views/pagLogin/");
            resolver.setSuffix(".html");
            resolver.setTemplateMode("HTML5");
            resolver.setOrder(0);
            resolver.setCacheable(false);
            return resolver;
        }

        @Bean 
        public SpringTemplateEngine templateEngine() {
            SpringTemplateEngine engine  =  new SpringTemplateEngine();
            engine.setTemplateResolver( templateResolver() );
            engine.setMessageSource( messageSource() );



            return engine;
        }

        @Bean 
        public ThymeleafViewResolver thymeleafViewResolver() {
            ThymeleafViewResolver resolver  =  new ThymeleafViewResolver();

            resolver.setTemplateEngine( templateEngine() );
            resolver.setOrder(1);

            resolver.setCache( false );
            return resolver;
        }

        @Bean
        public SpringResourceTemplateResolver thymeleafSpringResource() {
            SpringResourceTemplateResolver Vista = new SpringResourceTemplateResolver();
            Vista.setTemplateMode("HTML5");
            return Vista;
        }
}
_

私のapp-config初期化子

_public class InicializarApp extends AbstractAnnotationConfigDispatcherServletInitializer {

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

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

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] { new HiddenHttpMethodFilter() };
    }
}
_

ログインコントローラークラス

_@Controller
public class ControllerLogin {



    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String pageLogin(Model model) {



         return "login";
    }
_

私のホームコントローラークラス

_@Controller
public class HomeController {

        @RequestMapping(value = "/", method = RequestMethod.GET)
        public String home(Model model) {


        return "home";
        }


}
_

私のlogin.html

_<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://www.thymeleaf.org">
  <head>
    <title tiles:fragment="title">Messages : Create</title>
  </head>
  <body>
    <div tiles:fragment="content">
        <form name="f" th:action="@{/login}" method="post">               
            <fieldset>
                <legend>Please Login</legend>
                <div th:if="${param.error}" class="alert alert-error">    
                    Invalid username and password.
                </div>
                <div th:if="${param.logout}" class="alert alert-success"> 
                    You have been logged out.
                </div>

                <label for="username">Username</label>
                    <input type="text" id="username" name="username"/>        
                <label for="password">Password</label>
                    <input type="password" id="password" name="password"/>    

                <div class="form-actions">
                    <button type="submit" class="btn">Log in</button>
                </div>

                <!-- THIS IS COMMENTED it dont work beacuse i am already using thymeleaf <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>  -->


            </fieldset>
        </form>
    </div>


  </body>
</html>
_

私のhome.htmlページはログインした後にのみ表示され、セキュリティ設定クラスにput .csrf()。disable()がある場合にログインできる唯一の方法ですが、私がそれを入れていない場合、その保護を無効にしたくない私のセキュリティ設定クラスでは、この質問の冒頭で言及したエラーが表示されます。

15
stackUser2000

Spring Securityのドキュメント から

CSRF保護は、デフォルトでJava構成。CSRFを無効にする場合、対応するJava構成を参照できます。以下のJavadocを参照してください。 CSRF保護の構成方法の追加のカスタマイズについては、csrf()。

そして、CSRF保護が有効になっている場合

最後のステップは、すべてのPATCH、POST、PUT、およびDELETEメソッドにCSRFトークンを含めることを確認することです。

あなたの場合:

  • デフォルトでCSRF保護が有効になっています(Java configuration)を使用しているため)
  • hTTP POSTおよび
  • ログインフォームにCSRFトークンが含まれていません。このため、CSRF保護フィルターは着信要求でCSRFトークンを見つけることができないため、送信時にログイン要求は拒否されます。

可能な解決策はすでに決定済みです。

  1. http.csrf().disable();としてCSRF保護を無効にします。または
  2. ログインフォームに非表示パラメーターとしてCSRFトークンを含めます。

Thymeleafを使用しているため、ログインページのHTMLテンプレートで次のような操作を行う必要があります。

<form name="f" th:action="@{/login}" method="post">               
  <fieldset>

    <input type="hidden" 
           th:name="${_csrf.parameterName}" 
           th:value="${_csrf.token}" />

    ...
  </fieldset>
</form>

Thymeleaf CSRFプロセッサはキックするため、HTMLactionではなく、th:actionを使用する必要があることに注意してください。 -前者のみ。

フォーム送信方法をGETに変更するだけで問題を解決できますが、ユーザーがフォーム内の機密情報を送信するため、これはお勧めできません。

通常、Thymeleafフラグメントを作成します。これは、フォームを含むすべてのページで使用され、CSRFトークンを含むフォームのマークアップを生成します。これにより、アプリ全体の定型コードが削減されます。


@EnableWebMvcSecurityの代わりに@EnableWebSecurityを使用して、ThymeleafタグによるCSRFトークンの自動挿入を有効にします。また、Spring 3.2+およびThymeleaf 2.1+で<form th:action>の代わりに<form action>を使用して、ThymeleafにCSRFトークンを非表示フィールドとして自動的に含めるようにします(source Spring JIRA )。

39
manish

OPが望んでいたとおりに実装するソリューションを次に示します。

  1. @EnableWebSecurity@EnableWebMvcSecurity に置き換えます(これがOPの欠落です)
  2. th:actionタグで <form> を使用します

@EnableWebMvcSecurityを使用すると、Spring SecurityはCsrfRequestDataValueProcessorを登録し、th:actionを使用すると、thymeleafはgetExtraHiddenFieldsメソッドを使用して、追加の非表示フィールドをフォームに追加します。そして、csrfは追加の隠しフィールドです。

Spring Security 4.0以降 、@ EnableWebMvcSecurityは廃止され、@ EnableWebSecurityのみが必要です。 _ csrf保護は引き続き自動的に適用されます

15
name_no

ThymleafのSpring Securityダイアレクトを追加する必要があります。

1.)Spring Security Dialectモジュールをクラスパスに追加します。

Mavenの例:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity3</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>

2.)SpringTemplateDialectオブジェクトをSpringTemplateEngineに追加します

import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect;
templateEngine.addDialect(new SpringSecurityDialect()); //add this line in your config

ソース: Spring in Action 4th Edition

3

たぶん、その小さな情報の平和はだれでも助けになります。また、th:actionに起因するフォームを持つことも必須です。単純なHTML actionを属性付けするだけでは機能せず、ファイルされた非表示のCSRF入力は自動的に追加されません。

情報の安らぎがどこにも文書化されておらず、それについて2時間の調査に費やしたことがわかりませんでした。フォームにaction="#"を割り当て、Java script。CSRFトークン入力フィールドは、フォームにth:action="@{#}"を追加するまで自動的に追加されませんでした。今では魅力として機能します。

1
Mario Eis

以下を追加した後にのみ私のために働いた:

protected void configure(HttpSecurity http) throws Exception {
    ...

    http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    ...
}
0
Alex Rewa