问题
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 HttpFilters {
@Inject
CSRFFilter csrfFilter;
@Override
public EssentialFilter[] filters() {
return new EssentialFilter[]{csrfFilter.asJava()};
}}
Of course, it works, as long as all of requests need to be filtered, but some of them should be bypassed. How can filters be configured to bypass requests to some specified route? Thanks for your help!
回答1:
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
!
来源:https://stackoverflow.com/questions/37408269/play-2-5-disable-csrf-protection-for-some-requests