rest - Why is my spring boot stateless filter being called twice? -
i'm trying implement stateless token-based authentication on rest api i've developed using spring boot. idea client includes jwt token request, , filter extracts request, , sets securitycontext relevant authentication object based on contents of token. request routed normal, , secured using @preauthorize on mapped method.
my security config follows :
@configuration @enablewebsecurity @enableglobalmethodsecurity(prepostenabled = true) public class securityconfig extends websecurityconfigureradapter { @autowired private jwttokenauthenticationservice authenticationservice; @override protected void configure(httpsecurity http) throws exception { http .csrf().disable() .headers().addheaderwriter(new xframeoptionsheaderwriter(xframeoptionsmode.sameorigin)) .and() .authorizerequests() .antmatchers("/auth/**").permitall() .antmatchers("/api/**").authenticated() .and() .addfilterbefore(new statelessauthenticationfilter(authenticationservice), usernamepasswordauthenticationfilter.class); }
with stateless filter extends genericfilterbean defined follows :
public class statelessauthenticationfilter extends genericfilterbean { private static logger logger = logger.getlogger(statelessauthenticationfilter.class); private jwttokenauthenticationservice authenticationservice; public statelessauthenticationfilter(jwttokenauthenticationservice authenticationservice) { this.authenticationservice = authenticationservice; } @override public void dofilter( servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception { httpservletrequest httprequest = (httpservletrequest) request; authentication authentication = authenticationservice.getauthentication(httprequest); securitycontextholder.getcontext().setauthentication(authentication); logger.info("===== security context before request ====="); logger.info("request for: " + httprequest.getrequesturi()); logger.info(securitycontextholder.getcontext().getauthentication()); logger.info("==========================================="); chain.dofilter(request, response); securitycontextholder.getcontext().setauthentication(null); logger.info("===== security context after request ====="); logger.info("request for: " + httprequest.getrequesturi()); logger.info(securitycontextholder.getcontext().getauthentication()); logger.info("==========================================="); } }
and endpoint defined :
@preauthorize("hasauthority('user')") @requestmapping ( value="/api/attachments/{attachmentid}/{filename:.+}", method = requestmethod.get) public responseentity<byte[]> getattacheddocumentendpoint(@pathvariable string attachmentid, @pathvariable string filename) { logger.info("get called /attachments/" + attachmentid + "/" + filename); // file, , return responseentity<byte[]> object }
when doing on /api/attachments/someattachment/somefilename, including token, can see filter being invoked twice, once apparently token, , once without. restcontroller mapped request called once.
[info] [06-04-2015 12:26:44,465] [jwttokenauthenticationservice] getauthentication - getting authentication based on token supplied in http header [info] [06-04-2015 12:26:44,473] [statelessauthenticationfilter] dofilter - ===== security context before request ===== [info] [06-04-2015 12:26:44,473] [statelessauthenticationfilter] dofilter - request for: /api/attachments/1674b08b6bbd54a6efaff4a780001a9e/jpg.png [info] [06-04-2015 12:26:44,474] [statelessauthenticationfilter] dofilter - name:iser, principal:user, isauthenticated:true, grantedauthorites:[user] [info] [06-04-2015 12:26:44,474] [statelessauthenticationfilter] dofilter - =========================================== [info] [06-04-2015 12:26:44,476] [attachmentrestcontroller] getattacheddocumentendpoint - called /api/attachments/1674b08b6bbd54a6efaff4a780001a9e/jpg.png [info] [06-04-2015 12:26:44,477] [attachmentdbcontroller] getattachment - getattachment method called attachmentid:1674b08b6bbd54a6efaff4a780001a9e , , filename:jpg.png [info] [06-04-2015 12:26:44,483] [statelessauthenticationfilter] dofilter - ===== security context after request ===== [info] [06-04-2015 12:26:44,484] [statelessauthenticationfilter] dofilter - request for: /api/attachments/1674b08b6bbd54a6efaff4a780001a9e/jpg.png [info] [06-04-2015 12:26:44,484] [statelessauthenticationfilter] dofilter - [info] [06-04-2015 12:26:44,484] [statelessauthenticationfilter] dofilter - =========================================== [info] [06-04-2015 12:26:44,507] [jwttokenauthenticationservice] getauthentication - no token supplied in http header [info] [06-04-2015 12:26:44,507] [statelessauthenticationfilter] dofilter - ===== security context before request ===== [info] [06-04-2015 12:26:44,507] [statelessauthenticationfilter] dofilter - request for: /api/attachments/1674b08b6bbd54a6efaff4a780001a9e/jpg.png [info] [06-04-2015 12:26:44,507] [statelessauthenticationfilter] dofilter - [info] [06-04-2015 12:26:44,508] [statelessauthenticationfilter] dofilter - =========================================== [info] [06-04-2015 12:26:44,508] [statelessauthenticationfilter] dofilter - ===== security context after request ===== [info] [06-04-2015 12:26:44,508] [statelessauthenticationfilter] dofilter - request for: /api/attachments/1674b08b6bbd54a6efaff4a780001a9e/jpg.png [info] [06-04-2015 12:26:44,508] [statelessauthenticationfilter] dofilter - [info] [06-04-2015 12:26:44,508] [statelessauthenticationfilter] dofilter - ===========================================
what's going on here ?
edit :
it's stranger first thought - implementing simple endpoint returns simple message displays expected behaviour - it's seems when try return data responseentity above problem occur.
endpoint :
@preauthorize("hasauthority('user')") @requestmapping("/api/userhelloworld") public string userhelloworld() { return "hello secure user world"; }
output, showing single call filter (with debug on):
[info] [06-04-2015 19:43:25,831] [jwttokenauthenticationservice] getauthentication - getting authentication based on token supplied in http header [info] [06-04-2015 19:43:25,844] [statelessauthenticationfilter] dofilterinternal - ===== security context before request ===== [info] [06-04-2015 19:43:25,844] [statelessauthenticationfilter] dofilterinternal - request for: /api/userhelloworld [info] [06-04-2015 19:43:25,844] [statelessauthenticationfilter] dofilterinternal - response = null 200 [info] [06-04-2015 19:43:25,844] [statelessauthenticationfilter] dofilterinternal - name:user, principal:user, isauthenticated:true, grantedauthorites:[user] [info] [06-04-2015 19:43:25,845] [statelessauthenticationfilter] dofilterinternal - =========================================== [debug] [06-04-2015 19:43:25,845] [dispatcherservlet] doservice - dispatcherservlet name 'dispatcherservlet' processing request [/api/userhelloworld] [debug] [06-04-2015 19:43:25,847] [abstracthandlermethodmapping] gethandlerinternal - looking handler method path /api/userhelloworld [debug] [06-04-2015 19:43:25,848] [abstracthandlermethodmapping] gethandlerinternal - returning handler method [public java.lang.string restcontroller.userhelloworld()] [debug] [06-04-2015 19:43:25,849] [dispatcherservlet] dodispatch - last-modified value [/api/userhelloworld] is: -1 [debug] [06-04-2015 19:43:25,851] [abstractmessageconvertermethodprocessor] writewithmessageconverters - written [hello secure user world] "text/plain;charset=utf-8" using [org.springframework.http.converter.stringhttpmessageconverter@3eaf6fe7] [debug] [06-04-2015 19:43:25,852] [dispatcherservlet] processdispatchresult - null modelandview returned dispatcherservlet name 'dispatcherservlet': assuming handleradapter completed request handling [debug] [06-04-2015 19:43:25,852] [frameworkservlet] processrequest - completed request [info] [06-04-2015 19:43:25,852] [statelessauthenticationfilter] dofilterinternal - ===== security context after request ===== [info] [06-04-2015 19:43:25,853] [statelessauthenticationfilter] dofilterinternal - request for: /api/userhelloworld [info] [06-04-2015 19:43:25,853] [statelessauthenticationfilter] dofilterinternal - response = text/plain;charset=utf-8 200 [info] [06-04-2015 19:43:25,853] [statelessauthenticationfilter] dofilterinternal - [info] [06-04-2015 19:43:25,853] [statelessauthenticationfilter] dofilterinternal - ===========================================
okay - pretty ridiculous, seems it's issue way invoking request (via postman chrome extension)
postman seems fire in 2 requests, 1 headers, 1 without. there's open bug report describing here : https://github.com/a85/postman-chrome-extension/issues/615
the behaviour not seen if request invoked using curl, or straight browser.
Comments
Post a Comment