Angular 2 Spring Security CSRF令牌 [英] Angular 2 Spring Security CSRF Token

查看:76
本文介绍了Angular 2 Spring Security CSRF令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,我无法为我的应用设置安全解决方案!!
因此,我有一个REST API后端,该后端运行在 http:// localhost:51030 上,并使用Spring Framework开发,对于前端,我有一个Angular 2应用程序(最新版本为AKA Angular 4),该应用程序在 http:// localhost:4200上运行
我已经在后端中设置了CORS配置,如下所示:

Hi Everyone I'm having trouble setting up a security solution for my app!! So I have a REST API Backend which runs at http://localhost:51030 and developed with Spring Framework, and for the front side I have an Angular 2 application (the latest version A.K.A. Angular 4) which runs at http://localhost:4200. I have set the CORS configuration in the backend as seen below:

public class CORSFilter implements Filter
{
// The list of domains allowed to access the server
private final List<String> allowedOrigins = Arrays.asList("http://localhost:4200", "http://127.0.0.1:4200");

public void destroy()
{

}

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{   
    // Lets make sure that we are working with HTTP (that is, against HttpServletRequest and HttpServletResponse objects)
    if (req instanceof HttpServletRequest && res instanceof HttpServletResponse)
    {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        // Access-Control-Allow-Origin
        String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "");
        response.setHeader("Vary", "Origin");

        // Access-Control-Max-Age
        response.setHeader("Access-Control-Max-Age", "3600");

        // Access-Control-Allow-Credentials
        response.setHeader("Access-Control-Allow-Credentials", "true");

        // Access-Control-Allow-Methods
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");

        // Access-Control-Allow-Headers
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, " + CSRF.REQUEST_HEADER_NAME); // + CSRF.REQUEST_HEADER_NAME
    }
    chain.doFilter(req, res);
}


public void init(FilterConfig filterConfig)
{

}
}

使用此配置只能正常工作,我可以执行从角度应用到回弹的请求并获得响应并执行任何操作。
但是,当我尝试设置CSRF安全解决方案时,没有任何效果。
这是在后端设置的CSRF和安全配置:

Using this configuration only works fine, I can execute requests from the angular app to the spring back and get response and do anything. But when I try to set up CSRF security solution nothing works. This is the CSRF and Security configuration setted up in the backend:

public class CSRF
{

     /**
     * The name of the cookie with the CSRF token sent by the server as a response.
     */
     public static final String RESPONSE_COOKIE_NAME = "XSRF-TOKEN"; //CSRF-TOKEN

     /**
      * The name of the header carrying the CSRF token, expected in CSRF-protected requests to the server.
      */
    public static final String REQUEST_HEADER_NAME = "X-XSRF-TOKEN"; //X-CSRF-TOKEN

    // In Angular the CookieXSRFStrategy looks for a cookie called XSRF-TOKEN 
    // and sets a header named X-XSRF-TOKEN with the value of that cookie.

    // The server must do its part by setting the initial XSRF-TOKEN cookie 
    // and confirming that each subsequent state-modifying request includes 
    // a matching XSRF-TOKEN cookie and X-XSRF-TOKEN header.

}







public class CSRFTokenResponseCookieBindingFilter extends OncePerRequestFilter
{

    protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException
    {
        CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);

        Cookie cookie = new Cookie(CSRF.RESPONSE_COOKIE_NAME, token.getToken());
        cookie.setPath("/");

        response.addCookie(cookie);

        filterChain.doFilter(request, response);
    }
}







@Configuration
public class Conf extends WebMvcConfigurerAdapter
{
    @Bean
    public CORSFilter corsFilter()
    {
        return new CORSFilter();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry)
    {
        registry.addViewController("/login");
        registry.addViewController("/logout");
    }
}







@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{

    @Autowired
    private RESTAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private RESTAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private RESTAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private RESTLogoutSuccessHandler logoutSuccessHandler;

    @Resource
    private CORSFilter corsFilter;

    @Autowired
    private DataSource dataSource;


    @Autowired
    public void globalConfig(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.jdbcAuthentication()
            .dataSource(dataSource)
            .usersByUsernameQuery("select login as principal, password as credentials, true from user where login = ?")
            .authoritiesByUsernameQuery("select login as principal, profile as role from user where login = ?")
            .rolePrefix("ROLE_");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        //csrf is disabled for the moment
        //http.csrf().disable();

        //authorized requests
        http.authorizeRequests()
            .antMatchers("/api/users/**").permitAll()
            .antMatchers(HttpMethod.OPTIONS , "/*/**").permitAll()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated();

        //handling authentication exceptions
        http.exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint);

        //login configuration
        http.formLogin()
            .loginProcessingUrl("/login")
            .successHandler(authenticationSuccessHandler);
        http.formLogin()
            .failureHandler(authenticationFailureHandler);

        //logout configuration
        http.logout()
            .logoutUrl("/logout")
            .logoutSuccessHandler(logoutSuccessHandler);

        //CORS configuration
        http.addFilterBefore(corsFilter, ChannelProcessingFilter.class);


        //CSRF configuration
        http.csrf().requireCsrfProtectionMatcher(
                new AndRequestMatcher(
                // Apply CSRF protection to all paths that do NOT match the ones below

                // We disable CSRF at login/logout, but only for OPTIONS methods to enable the browser preflight
                new NegatedRequestMatcher(new AntPathRequestMatcher("/login*/**", HttpMethod.OPTIONS.toString())),
                new NegatedRequestMatcher(new AntPathRequestMatcher("/logout*/**", HttpMethod.OPTIONS.toString())),

                new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.GET.toString())),
                new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.HEAD.toString())),
                new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.OPTIONS.toString())),
                new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.TRACE.toString()))
            )
        );

        // CSRF tokens handling
        http.addFilterAfter(new CSRFTokenResponseCookieBindingFilter(), CsrfFilter.class);

    }
}






问题出在正面和4角配置中,CSRF文档非常差,并且在Internet中没有完整的CSRF实现示例。
下面是我的登录服务:


The problem is in the front side and the angular 4 configuration, the CSRF documentation is so poor and there is no full example of CSRF implementation in the Internet. So below is my login service:

@Injectable()
export class LoginService {

    private loginUrl = 'http://localhost:51030/login';

    constructor(private http: Http) {}

    preFlight() {
        return this.http.options(this.loginUrl);
    }

    login(username: string , password: string) {

        let headers = new Headers();

        headers.append('Content-Type', 'application/x-www-form-urlencoded');

        let options = new RequestOptions({headers: headers});

        let body = "username="+username+"&password="+password;

        return this.http.post(this.loginUrl , body , options);

    }
}






然后在登录组件中执行ngOnInit生命周期挂钩中的选项请求:


And in the login component I execute the option request in the ngOnInit life cycle hook:

@Component({
    templateUrl: './login-layout.component.html'
})
export class LoginLayoutComponent implements OnInit {

    credentials = {username: '' , password: ''};

    constructor(private loginService: LoginService){}

    ngOnInit() {
        this.loginService.preFlight()
                         .subscribe();
    }

    login() {
        this.loginService.login(this.credentials.username , this.credentials.password)
                         .subscribe(
                            response=>{
                                console.log(response) ; 
                            },error=>{
                                console.log(error);
                            }
                         );
    }

}






预检顺利进行,我在选项请求中获得200 OK状态,以及一个临时的JSEEIONID和XSRF-TOKEN Cookie。


The preflight goes well and I get the 200 OK status on the options request plus a temporary JSEEIONID and the XSRF-TOKEN Cookie.

因此在我的应用程序模块中我在有角度的文档中这样说:

So in my app module I added this as said in the angular docs:

{
    provide: XSRFStrategy,
    useValue: new CookieXSRFStrategy('XSRF-TOKEN', 'X-XSRF-TOKEN')
  },

但是,当我尝试使用凭证或任何向后的请求执行POST请求时,我收到403 Forbidden:由于未找到您的会话,因此无法验证提供的CSRF令牌。

BUT, when I try to execute a POST request with the credentials or any request to the back I got 403 Forbidden: "Could not verify the provided CSRF token because your session was not found."

所以,请问我该如何解决这个问题,有人能指出我正确的方向吗,因为我不知道如何进行这项工作!
谢谢!

So Please how can I solve this, can any one point me to right direction cause I have no clue on how to make this work!! And Thanks!!!

推荐答案

要解决弹簧安全性和角度之间的csrf问题,您必须这样做

To solve the csrf problem between spring security and angular, you have to do that.

在SecurityConfiguration(WebSecurityConfig)中,替换http.csrf()。disable();。通过

In SecurityConfiguration (WebSecurityConfig),replace http.csrf().disable(); by

               http.csrf()
                .ignoringAntMatchers ("/login","/logout")
                .csrfTokenRepository (this.getCsrfTokenRepository());


    }
    private CsrfTokenRepository getCsrfTokenRepository() {
        CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
        tokenRepository.setCookiePath("/");
        return tokenRepository;
{

默认的角度csrf拦截器并不总是有效,因此您必须实现

the default angular csrf interceptor does not always work.So you have to implement your own interceptor.

import {Injectable, Inject} from '@angular/core';
import {HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler,
  HttpEvent} from '@angular/common/http';
import {Observable} from "rxjs";


@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {

  constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    let requestMethod: string = req.method;
    requestMethod = requestMethod.toLowerCase();

    if (requestMethod && (requestMethod === 'post' || requestMethod === 'delete' || requestMethod === 'put')) {
      const headerName = 'X-XSRF-TOKEN';
      let token = this.tokenExtractor.getToken() as string;
      if (token !== null && !req.headers.has(headerName)) {
        req = req.clone({headers: req.headers.set(headerName, token)});
      }
    }

    return next.handle(req);
  }
}

最后将其添加到您的提供程序中(app.module .ts)

And finally add it in your providers (app.module.ts)

providers: [{ provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptor, multi: true }]

考虑放入您的进口商品。

Think about putting in your imports.

   HttpClientXsrfModule.withOptions({
      cookieName: 'XSRF-TOKEN',
      headerName: 'X-CSRF-TOKEN'
    }),

这篇关于Angular 2 Spring Security CSRF令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆