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.
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.)
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.
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.