Authentication from Android device against Spring Security API

I have a SPRING Backend with a REST API. It is protected by username and password. When I open it using my pc browser, the login screen first opens, and after I added the credentials, I can access the api.

When I try to do the same through the Android APP, I get it every time I refer to the login screen. For authorization on the Android side, I use the REST API request, which is accessible by default. Is the built-in Android application browser incompatible for storing session files? Every time a new HTTP session is created. I use Volley for queries

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> 

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. 

salvo

  CookieManager manager = new CookieManager(); CookieHandler.setDefault( manager ); mQueue = Volley.newRequestQueue(context); 
+8
java spring android rest spring-mvc
source share
2 answers

I suspect the problem is that you are implementing your own authentication controller endpoint and not using the whole spring-security chain.

A normal filter chain will still be called in all requests according to your xml configuration, but just calling the authenticate () method in the controller will just do it, and not the rest of the contents of the authentication suxy handler, for example. you will not actually set a cookie in the response with your adhoc controller authentication)

The easiest way to verify this is to simply twist the URL directly or use something like postman (chrome plugin for rest apis) to verify api authentication and see if any cookeis are configured to respond.

If you have control over the serveride code (for example, you can change it and not just work with the Android application), here are a few thoughts:

  • I would avoid user authentication endpoints and try to manually hide the security elements - spring security is really good at that.

  • Assuming you don't want to go with oauth and the complexity of these API solutions, then take a look at Google's recommended approach for protecting APIs for mobile apps . This is similar to the approach you took, but to log in, you simply embed the web view in your Android application and use it to let the user enter the standard spring security form (so use spring - the default security behavior, for example, files cookie, etc.), then in the application you simply extract the user's token from the web response and then save it - which can then be used in all API requests as the request header (therefore, the user does not need to log in to the mob Flax application)

I wrote a review of the approach here:

Securing Your Mobile Access API

and spring security implementation is also implemented here.

Protect your mobile API with spring Security

+9
source share

I tried your offer and got his job.

After I verify that the backend is transferring the necessary cookie, I configure the volley to save the transmitted cookie and retransmit it for each request after that as a token. The cookie is saved in the settings.

 public GsonRequest(...) ..... if(PreferencesManager.getInstance().getSessionCookie()!=null) this.headers.put("Cookie", "JSESSIONID="+ PreferencesManager.getInstance().getSessionCookie()); @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { String cookie = MyApp.get().checkSessionCookie(response.headers); PreferencesManager.getInstance().setSessionCookie(cookie); ..... } 

as indicated by vmirinov on go here with weak modification

 public final String checkSessionCookie(Map<String, String> headers) { if (headers.containsKey(SET_COOKIE_KEY) && headers.get(SET_COOKIE_KEY).toLowerCase().contains(SESSION_COOKIE)) { String cookie = headers.get(SET_COOKIE_KEY); if (cookie.length() > 0) { String[] splitCookie = cookie.split(";"); String[] splitSessionId = splitCookie[0].split("="); cookie = splitSessionId[1]; SharedPreferences.Editor prefEditor = _preferences.edit(); prefEditor.putString(SESSION_COOKIE, cookie); prefEditor.commit(); return cookie; } } return ""; } 
0
source share

All Articles