i have an SPRING Backend with an REST-API. It is secured by username and password. When i open it with my pc browser at first the login screen is shown and after i added the credentials i can access the api fluendly.
When i try the same through an Android APP, i get every time referred to the login screen. To auth on the Android Side i use an REST API Request which is by default accessable. Is the internally Android App Browser not compatible to store session cookies? Everytime a new HTTP session gets created. Im using Volley for the requests
spring-security.xml
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/api/user/login" access="permitAll" /> <!--IS_AUTHENTICATED_ANONYMOUSLY-->
<intercept-url pattern="/admin/**" access="hasAnyRole('ROLE_ADMIN','ROLE_GROUP_LEADER')" />
<intercept-url pattern="/api/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN','ROLE_GROUP_LEADER')" />
<form-login
login-page="/login"
default-target-url="/admin"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password"
/>
<access-denied-handler error-page="/403" />
<logout logout-success-url="/login?logout" />
</http>
<authentication-manager alias="authManager">
<authentication-provider >
<password-encoder ref="encoder" />
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query=
"select username,password, enabled from user where username=?"
authorities-by-username-query=
"select username, name as role from role r,user u where u.role_id = r.id and username =? " />
</authentication-provider>
</authentication-manager>
the code in the controller from the rest api
UserDetails userDetails = userDetailsSvc.loadUserByUsername(user.getUsername());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, "123",userDetails.getAuthorities());
Authentication authentication = authManager.authenticate(token);
log.debug("Logging in with [{}]", authentication.getPrincipal());
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
Spring Security DEBUG
************************************************************
Request received for POST '/api/user/create':
org.apache.catalina.connector.RequestFacade@36dc8ced
servletPath:/api/user/create
pathInfo:null
headers:
if-modified-since: Sat, 03 Jan 2015 12:35:50 GMT+00:00
content-type: application/json; charset=utf-8
user-agent: Dalvik/1.6.0 (Linux; U; Android 4.0.4; GT-P7100 Build/IMM76D)
host: 192.168.178.36:8088
connection: Keep-Alive
accept-encoding: gzip
content-length: 124
Security filter chain: [
SecurityContextPersistenceFilter
WebAsyncManagerIntegrationFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2015-01-03 13:53:46 INFO Spring Security Debugger:39 -
************************************************************
New HTTP session created: A430DD754F7F6E466D07B10D1DDCCEF7
Call stack:
at org.springframework.security.web.debug.Logger.info(Logger.java:29)
at org.springframework.security.web.debug.DebugRequestWrapper.getSession(DebugFilter.java:144)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:238)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:238)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:238)
at org.springframework.security.web.savedrequest.HttpSessionRequestCache.saveRequest(HttpSessionRequestCache.java:40)
at org.springframework.security.web.access.ExceptionTranslationFilter.sendStartAuthentication(ExceptionTranslationFilter.java:184)
at org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:168)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:131)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:70)
at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:59)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:74)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1015)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:652)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1575)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1533)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
************************************************************
2015-01-03 13:53:46 INFO Spring Security Debugger:39 -
************************************************************
Request received for GET '/login':
org.apache.catalina.connector.RequestFacade@36dc8ced
servletPath:/login
pathInfo:null
headers:
if-modified-since: Sat, 03 Jan 2015 12:35:50 GMT+00:00
content-type: application/json; charset=utf-8
user-agent: Dalvik/1.6.0 (Linux; U; Android 4.0.4; GT-P7100 Build/IMM76D)
host: 192.168.178.36:8088
connection: Keep-Alive
accept-encoding: gzip
Security filter chain: [
SecurityContextPersistenceFilter
WebAsyncManagerIntegrationFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
MonitorFilter::WARNING: the monitor filter must be the first filter in the chain.
volley
CookieManager manager = new CookieManager();
CookieHandler.setDefault( manager );
mQueue = Volley.newRequestQueue(context);
I suspect the problem may be that you are implementing your own custom authentication controller endpoint and not using the full spring-security chain.
The normal filter chain will still get invoked on all requests as per your xml config, but by just calling authenticate() method in a controller will just do that, and not the rest of the authentication sucess handler stuff e.g. you won't actually be setting a cookie on the response with your adhoc controller authentication)
The easiest way to test this is just curl the url directly, or use something like postman (chrome plugin for rest apis) to test the api authentication endpoit and see if any cookeis are being set on the response.
If you have control over the serverside code (e.g. you can change it and aren't just working on the android app), here are a few thoughts:
I would avoid custom authentication endpoints and trying to hand-roll the security stuff - spring security is really good at that stuff.
Assuming you don't want to go with oauth and the complexity of that solutions for the API, then take a look at Google's recommended approach for securing APIs for mobile apps. It's similar to approach you have taken, but for the login you just embed a web view in the android app and use it to let the user login directly on the standard spring security form (so get to take advantage of Spring-security default behaviour e.g. cookies etc), then in the app you just grab a user token from the web response and then store that - which can then be used in all API requests as a request header (so user doesnt have to keep logging in on the mobile app)
I wrote up an overview of the approach here:
Securing your API for mobile access
and have put together a spring security implementation here as well.
Securing your mobile API with Spring Security
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With