rbo*_*sov 4 spring csrf spring-security angular
我是 Spring Security 的新手,我正在尝试了解 CSRF 机制。我有一个基于 Spring 的 Angular 应用程序。据我所知,Spring 将在收到的第一个 GET 请求(称为 XSRF-TOKEN)上的 cookie 中发送 CSRF 令牌,然后在每个后续请求中,它将在另一个名为(X-XSRF-TOKEN)的 cookie 中查找该令牌)。我的问题是它没有生成令牌,我不明白为什么。我正在使用最新的 Spring 和 Angular 版本。
我创建了一个虚拟应用程序,其中有一个 /home 端点,它接受 GET 请求,然后返回一个虚拟字符串。我已经在 Angular 端设置了 withCredentials: true 并配置了 CookieCsrfTokenRepository。我没有看到包含令牌的 Set-cookie 标头。我应该做什么以及我做错了什么?
Spring安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
.permitAll()
.requestMatchers("/home")
.permitAll()
.anyRequest()
.authenticated();
return http.build();
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control-Allow-Origin", "Content-Type",
"Accept", "Authorization", "Origin, Accept", "X-Request-With", "Access-Control-Request-Method",
"Access-Control-Request-Headers", "XSRF-TOKEN", "X-XSRF-TOKEN"));
corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization",
"Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", "XSRF-TOKEN", "X-XSRF-TOKEN"));
corsConfiguration.setAllowedMethods(Arrays.asList(
HttpMethod.GET.name(), HttpMethod.POST.name(),
HttpMethod.PUT.name(), HttpMethod.PATCH.name(),
HttpMethod.DELETE.name(), HttpMethod.OPTIONS.name()
));
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new org.springframework.web.filter.CorsFilter(urlBasedCorsConfigurationSource);
}
}
Run Code Online (Sandbox Code Playgroud)
家庭控制器
@RestController
public class HomeResource {
@GetMapping("/home")
public String home() {
return "Home is working";
}
}
Run Code Online (Sandbox Code Playgroud)
角
主页.service.ts
@Injectable({
providedIn: 'root'
})
export class HomeService {
apiHost: string = 'http://www.localhost:8000';
constructor(private http: HttpClient) {
}
public home(): Observable<string> {
return this.http.get(`${this.apiHost}/home`, {withCredentials: true, responseType: 'text'});
}
}
Run Code Online (Sandbox Code Playgroud)
home.component.html
<p>{{home$ | async}}</p>
Run Code Online (Sandbox Code Playgroud)
home.component.ts
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
home$!: Observable<string>;
constructor(private homeService: HomeService) {
}
ngOnInit(): void {
this.home$ = this.homeService.home();
}
}
Run Code Online (Sandbox Code Playgroud)
请参阅此迁移文档,并完整阅读。
总之,Spring Security 6.0 推迟了 的加载CsrfToken,这意味着它不会在响应中自动包含令牌。
您有几种方法可以解决这个问题:
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null);
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
Run Code Online (Sandbox Code Playgroud)
CsrfToken创建一个从请求中检索的控制器端点:@RestController
@RequestMapping("/csrf")
public class CsrfController {
@GetMapping
public void getCsrfToken(HttpServletRequest request) {
// https://github.com/spring-projects/spring-security/issues/12094#issuecomment-1294150717
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
csrfToken.getToken();
}
}
Run Code Online (Sandbox Code Playgroud)
你可以考虑一下http.requestMatchers("/csrf").permitAll()。
在所有情况下,您只需从请求属性访问 CsrfToken 即可。
并且,在 Angular 方面,您可以创建一个APP_INITIALIZER提供程序,该提供程序将在应用程序初始化时获取 CSRF 令牌:
function getCsrfToken(httpClient: HttpClient): () => Observable<any> {
return () => httpClient.get('/csrf').pipe(catchError((err) => of(null)));
}
@NgModule({
...
providers: [
{
provide: APP_INITIALIZER,
useFactory: getCsrfToken,
deps: [HttpClient],
multi: true,
},
],
...
})
export class AppModule {}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5156 次 |
| 最近记录: |