How to add csrf token to the html form?

19,653

I tried different ways and eventually I've found the solution:

Previously index.html was located in the folder /resources/static

I put it in the folder /resources/templates. Also I've added dependency:

compile('org.thymeleaf:thymeleaf-spring5');

and explicitly declared the controller:

@RequestMapping("/")
public String index(){
   return "index";
} 

index.html look like this:

<html lang="en" ng-app="springChat" xmlns:th="http://www.springframework.org/schema/beans">
<head>
    ...
    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_parameter_name" th:content="${_csrf.parameterName}"/>

....
</body>     
<script type="application/javascript">

    $('#login-form').submit(function (ev) {
        ev.preventDefault(); // to stop the form from submitting
        var token = $("meta[name='_csrf']").attr("content");
        var paramName = $("meta[name='_csrf_parameter_name']").attr("content");
        $('<input>').attr('type', 'hidden').attr('name', paramName).attr('value', token).appendTo('#login-form');

        this.submit(); 
    });
</script>
</html>

Looks like spring boot searches in /resources/static raw html if we don't have explicitly declared mapping and doesn't try to insert values like th:content="${_csrf.token}" into variables.

Share:
19,653

Related videos on Youtube

gstackoverflow
Author by

gstackoverflow

Updated on June 04, 2022

Comments

  • gstackoverflow
    gstackoverflow almost 2 years

    After I enabled csrf(removed line .csrf().disable()) in my application my login request stopped working - /login POST redirects me to the home page:

    request looks like this:
    enter image description here

    On the page I have following js:

    <script type="text/javascript" src="/webjars/js-cookie/js.cookie.js"></script>
    ...
    $('#login-form').submit(function (ev) {
        ev.preventDefault(); // to stop the form from submitting
        var headerName = "X-CSRF-TOKEN";
        var token = Cookies.get('XSRF-TOKEN')
        $('<input>').attr('type', 'hidden').attr('name', '_csrf').attr('value', Cookies.get('XSRF-TOKEN')).appendTo('#login-form');
        this.submit(); // If all the validations succeeded
        })
    

    Can you clarify what am doing wrong?

    P.S.

    I read a lot of related stuff and everywhere I found lines like this:

    <meta name="_csrf" content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    

    I don't understand how should I set value _csrf.headerName

    I tried to add these data to meta bit its doesn't resolve:
    enter image description here

    and I tried to add it to the form directly:

    enter image description here

    And no success again.

    Additionally i tried this:

    enter image description here

    But as you can see - it doesn't resolve my attributes too

    P.S.2

    configuration:

    EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        private static final String SECURE_ADMIN_PASSWORD = "rockandroll";
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .formLogin()
                    .loginPage("/index.html")
                        .loginProcessingUrl("/login")
                        .defaultSuccessUrl("/sender.html")
                        .permitAll()
                    .and()
                    .logout()
                        .logoutSuccessUrl("/index.html")
                        .permitAll()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/js/**", "/lib/**", "/images/**", "/css/**", "/index.html", "/","/*.css","/webjars/**", "/*.js").permitAll()
                    .antMatchers("/websocket").hasRole("ADMIN")
                    .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN")
                    .anyRequest().authenticated();
    
        }
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    
            auth.authenticationProvider(new AuthenticationProvider() {
    
                @Override
                public boolean supports(Class<?> authentication) {
                    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
                }
    
                @Override
                public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                    UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
    
                    List<GrantedAuthority> authorities = SECURE_ADMIN_PASSWORD.equals(token.getCredentials()) ?
                            AuthorityUtils.createAuthorityList("ROLE_ADMIN") : null;
    
                    return new UsernamePasswordAuthenticationToken(token.getName(), token.getCredentials(), authorities);
                }
            });
        }
    }
    

    build.gradle:

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE")
        }
    }
    
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    
    jar {
        baseName = 'gs-messaging-stomp-websocket'
        version = '0.1.0'
    }
    sourceCompatibility = 1.8
    targetCompatibility = 1.8
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        compile("org.springframework.boot:spring-boot-starter-websocket")
        compile("org.webjars:webjars-locator-core")
        compile("org.webjars:sockjs-client:1.0.2")
        compile("org.webjars:stomp-websocket:2.3.3")
        compile("org.webjars:bootstrap:3.3.7")
        compile("org.webjars:jquery:3.1.0")
        compile("org.webjars:js-cookie:2.1.0")
    
        compile("org.springframework.boot:spring-boot-starter-actuator")
        //security
        compile ('org.springframework.security:spring-security-messaging')
        compile group: 'org.springframework.security', name: 'spring-security-web'
        compile group: 'org.springframework.security', name: 'spring-security-config'
    
        testCompile("org.springframework.boot:spring-boot-starter-test")
    }
    
    • ikos23
      ikos23 about 6 years
      Ok, I can see U added more info. I'd like to ask you to add more details about what error you are getting (or what behavior you expect but it does not work that way).
    • holmis83
      holmis83 about 6 years
      Do you use server template language JSP or Thymeleaf?
    • gstackoverflow
      gstackoverflow about 6 years
      @holmis83 I use raw html
    • gstackoverflow
      gstackoverflow about 6 years
      @john Result the same). /login responds 302 instead of successful login. Login happens successfuly in case of I disable csrf explicitly(.csrf().disable())
  • gstackoverflow
    gstackoverflow about 6 years
    I use raw html so I can't use it
  • venkatReddi
    venkatReddi over 5 years
    This solution works, but do you know why in a jsp form we can send that a form data, but when we are doing Ajax we need to add it as a header instead of form data in body? Confusing
  • Guru
    Guru over 4 years
    thanks! just to get attention th: is very important prefix here.