简体   繁体   中英

Spring Security not creating CSRF token

I wish to use Spring Security (version 5.1.2) to generate a CSRF token for my Angular 7 application. I have the following in my SecurityConfig file:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and()
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}

with the following RequestMapping in my controller:

@RestController
@RequestMapping("/authentication")
public class AuthenticationController {

    @GetMapping("/csrf")
    public void getCsrfToken(){...}

    @PostMapping("/register")
    public RegisterOutputDTO register(@RequestBody UserDTO input){...}
}

I gathered from various sources that the csrfTokenRepository would automatically generate a cookie with header XSRF-token on my first GET call (which is what /authentication/csrf is for), but I am not getting a cookie back from the server. Hence on my next POST call I am getting a 403 response. What could I possibly be missing?

这是我的输出

As indicated on the comments to my question, I found the answer to my problem. A cookie can not be sent cross-domain.

My frontend was deployed on localhost:3000 and my backend on localhost:9080 , which are considered different domains apparently. If I go to localhost:9080 (I get a white page, but that doesn't matter) and I then go to the application tab in Chrome, I find that the XSRF cookie I was looking for is stored like I was expecting all along. The cookie was available from the GET call I executed from my front-end. The problem is that the cookie needs to be available for localhost:3000 so that Angular can make use of the cookie.

There are multiple ways you can solve this issue in your local environment.

Use a proxy

You can use a proxy to map certain url paths to your backend. This is the solution I went with.

I run my Angular application with a webpack dev server. Webpack provides an easy way to proxy certain urls to your backend. For example:

devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 3000,
    proxy: {
        '/api': 'http://localhost:9080'
    }
}

The Angular application runs on localhost:3000 . Any calls to localhost:3000/api/* . will be forwarded to localhost:9080/api/* . So in my case I no longer perform a GET call on localhost:9080/api/authentication/csrf , but I call localhost:3000/api/authentication/csrf which will then get forwarded to my backend. (I added /api to the path in my rest controller, for those wondering.)

Deploy both applications on the same port

Using the frontend-maven-plugin you can build the frontend to its dist folder and then let maven package the dist folder along with the backend for deploy. I haven't tried this, but there are various resources that show this should be easy to do with Spring boot. So both frontend and backend would be available through localhost:9080 for example.

Use Spring Profile to disable csrf locally

You can make use of Spring @Profile annotations to create a different configuration for local environment and the rest (test, acceptance, production). Csrf can simply be disabled for development. I do not prefer this option since I like to keep DEV and other environments the same as much as possible. It's also not a real answer to the problem statement.

Special thanks to the answer of user @dspies which helped me find the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM