Play 2.5 disable csrf protection for some requests

后端 未结 1 1833
旧巷少年郎
旧巷少年郎 2021-01-14 05:25

I\'m writing my app using play framework v. 2.5.3 and use CSRF protection as it is described in official documentation.

public class Filters implements HttpF         


        
相关标签:
1条回答
  • 2021-01-14 05:55

    You can decorate CSRFFilter and use a list of route paths to either include or exclude the application of the filter.

    The route paths will need to be in the compiled form, so a route like ´/foo/bar´ would be /profile but a route with dynamic components like /view/:foo/:bar becomes /view/$foo<[^/]+>/$bar<[^/]+>. You can list the compiled versions of the routes by going to an unmapped URL (e.g. http://localhost:9000/@foo) when in development mode.

    import java.util.LinkedList;
    import java.util.List;
    import javax.inject.Inject;
    import akka.util.ByteString;
    import play.filters.csrf.CSRFFilter;
    import play.libs.streams.Accumulator;
    import play.mvc.EssentialAction;
    import play.mvc.EssentialFilter;
    import play.mvc.Result;
    import play.routing.Router;
    
    public class MaybeCsrfFilter extends EssentialFilter {
    
        private final EssentialFilter csrfFilter;
    
        private final List<String> applyCsrf = new LinkedList<>();
    
        @Inject
        public MaybeCsrfFilter(final CSRFFilter csrfFilter) {
            this.csrfFilter = csrfFilter.asJava();
    
            // alternatively, define the inclusion/exclusion list in the config and inject Configuration to obtain it
            applyCsrf.add("/foo/bar");
            applyCsrf.add("/view/$foo<[^/]+>/$bar<[^/]+>");
        }
    
        @Override
        public EssentialAction apply(final EssentialAction next) {
            return EssentialAction.of(request -> {
                final Accumulator<ByteString, Result> accumulator;
                final String currentRoute = request.tags().get(Router.Tags.ROUTE_PATTERN);
                if (applyCsrf.contains(currentRoute)) {
                    accumulator = csrfFilter.apply(next).apply(request);
                } else {
                    accumulator = next.apply(request);
                }
                return accumulator;
            });
        }
    }
    

    It's brute force, and you have to keep your filters in sync with the inclusion/exclusion list, but it works.

    Alternatively, you can use comments in the routes file to determine which routes should not have the CSRF filter applied.

    For a routes file like

    #NOCSRF
    GET   /foo/bar               controllers.Application.foo()
    #NOCSRF
    GET   /view/:hurdy/:gurdy    controllers.Application.bar()
    GET   /something/else        controllers.Application.bar()
    

    This filter implementation will not apply the CSRF filter to any action whose route is preceded by # NOCSRF. For this example, only /something/else will have the CSRF filter applied to it.

    public EssentialAction apply(final EssentialAction next) {
        return EssentialAction.of(request -> {
            final Accumulator<ByteString, Result> accumulator;
            final String routeComment = request.tags().get(Router.Tags.ROUTE_COMMENTS);
            if ("NOCSRF".equals(routeComment)) {
                accumulator = next.apply(request);
            } else {
                accumulator = csrfFilter.apply(next).apply(request);
            }
            return accumulator;
        });
    }
    

    Your Filters definition then becomes

    public class Filters implements HttpFilters {
    
        private final MaybeCsrfFilter csrf;
    
        @Inject
        public Filters(final MaybeCsrfFilter csrf) {
            this.csrf = csrf;
        }
    
        @Override
        public EssentialFilter[] filters() {
            return new EssentialFilter[]{csrf};
        }
    }
    

    Don't forget to create a binding for MaybeCsrfFilter!

    0 讨论(0)
提交回复
热议问题