Spring CSRF + AngularJs

I've tried many of the contributors to this thread already, none works for me.

I have a basic CRUD with Spring MVC 4.1.7, Spring Security 3.2.3 running on MySQL + Tomcat7.

The problem is that when I try to create a POST form using AngularJS, I get blocked on a 403 (access denied) error.

I figured out that I need to send a CSRF_TOKEN with a POST request, but I can't figure out HOW!

I've tried so many different ways and none seem to work.

My files

Controller.js

$scope.novo = function novo() {
  if($scope.id){
   alert("Update - " + $scope.id);
  }
  else{
      var Obj = {
              descricao : 'Test',
              saldo_inicial : 0.00,
              saldo : 33.45,
              aberto : false,
              usuario_id : null,
              ativo : true
      };
      $http.post(urlBase + 'caixas/adicionar', Obj).success(function(data) {
          $scope.caixas = data;    
      }).error(function(data) {alert(data)});
  }
 };

      

Spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">

<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/seguro**"
        access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
    <intercept-url pattern="/seguro/financeiro**"
        access="hasAnyRole('ROLE_FINANCEIRO','ROLE_ADMIN')" />

    <!-- access denied page -->
    <access-denied-handler error-page="/negado" />
    <form-login login-page="/home/" default-target-url="/seguro/"
        authentication-failure-url="/home?error" username-parameter="inputEmail"
        password-parameter="inputPassword" />
    <logout logout-success-url="/home?logout" />
    <!-- enable csrf protection -->
    <csrf />
</http>

<!-- Select users and user_roles from database -->
<authentication-manager>
    <authentication-provider>
        <password-encoder hash="md5" />
        <jdbc-user-service data-source-ref="dataSource"
            users-by-username-query="SELECT login, senha, ativo
               FROM usuarios 
              WHERE login = ?"
            authorities-by-username-query="SELECT u.login, r.role
               FROM usuarios_roles r, usuarios u
              WHERE u.id = r.usuario_id
                AND u.login = ?" />
    </authentication-provider>
</authentication-manager>

      

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<display-name>Barattie ~ Soluções Integradas</display-name>

<!-- The definition of the Root Spring Container shared by all Servlets 
    and Filters -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/spring-security.xml
        /WEB-INF/spring/spring-database.xml
        /WEB-INF/spring/spring-hibernate.xml
    </param-value>
</context-param>
<context-param>
    <param-name>com.sun.faces.writeStateAtFormEnd</param-name>
    <param-value>false</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Processes application requests -->
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/home</url-pattern>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- Spring Security -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/erro</location>
</error-page>

      

UPDATE

I tried adding for client side rename xsrf, but I refuse access all the time.

var app = angular.module('myApp', []).config(function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = '_csrf';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
});

      

** UPDATE 2 **

I tried to implement a filter like this.

package sys.barattie.util;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.filter.OncePerRequestFilter;

public class CsrfFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
    if (csrf != null) {
        Cookie cookie = new Cookie("XSRF-TOKEN", csrf.getToken());
        cookie.setPath("/");
        response.addCookie(cookie);
    }
    filterChain.doFilter(request, response);
}
}

      

And changed my Spring security.

     <csrf token-repository-ref="csrfTokenRepository" />
<beans:bean id="csrfTokenRepository" class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository">
    <beans:property name="headerName" value="X-XSRF-TOKEN" />
</beans:bean>

      

web.xml

<filter>
    <filter-name>csrfFilter</filter-name>
    <filter-class>sys.barattie.util.CsrfFilter</filter-class>
</filter>

      

But it looks like the server is not running the filter, I added System.out.println to it, but I don't see any messages in the debug

+3


source to share


4 answers


I did some tests and ended up with this.

In my login controller, if my user authentication succeeds, I create a token named AngularJS token in the same way.

CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
    Cookie cookie = new Cookie("XSRF-TOKEN", csrf.getToken());
    cookie.setPath("/");
    response.addCookie(cookie);
}

      



After that, I can manage my $ http.post successfully.

I don't know if this is the best way, but this is how it works for me!

Thanks for the help @Joao Evangelista p>

+3


source


The main issue is integration, Angular always looks for the XSRF-TOKEN cookie name and Spring sends CSRF_TOKEN, you need to provide a filter to change this. Something like that:

private static Filter csrfHeaderFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request,
                                            HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {
                CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName())
                if (csrf != null) {
                    Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN")
                    String token = csrf.getToken()
                    if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                        cookie = new Cookie("XSRF-TOKEN", token)
                        cookie.setPath("/")
                        response.addCookie(cookie)
                    }
                }
                filterChain.doFilter(request, response)
            }
        }
    }

    static CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository()
        repository.setHeaderName("X-XSRF-TOKEN")
        return repository
    }

      

NOTICE The only example I have is written in Groovy but just missing some ;

Then you need to add the filter and repository to the properties <csrf/>

, you can explicitly set the class that implements the filter as <bean>

using @Component

or declaring these methods as @Bean

in the config class



Otherwise, you can change the config $http

to Angular as per the docs setting these parameters according to Spring name and token header name

xsrfHeaderName – {string} – Name of HTTP header to populate with the XSRF token.
xsrfCookieName – {string} – Name of cookie containing the XSRF token.

      

You can check out more about this in the Usage docs under

0


source


i also ran into this problem and this is the solution that works for me (from Spring documentation), please note that im not using $ http. I use $ resource instead. 'You can ask Spring to save a cookie with Angular's default:

<csrf token-repository-ref="tokenRepository"/>

      

...    </http>

<b:bean id="tokenRepository"
    class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
    p:cookieHttpOnly="false"/>

      

Note that if you are using $ http there is a field in the config file called xsrfCookieName where you can explicitly specify the cookie name. But I havent tried this option, so I don't know how useful it is.

Regards.

0


source


This should work:

A class that extends WebSecurityConfigurerAdapter:

@Override
protected void configure(HttpSecurity http) throws Exception {

    http
            .csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
..

      

And then in your AngularJS, you can use any module for csrf, etc .: spring-security-csrf-token-interceptor

0


source







All Articles