2015-01-03 14 views
8

Possiedo un backend SPRING con un'API REST. È protetto da nome utente e password. Quando lo apro con il mio browser pc, inizialmente viene mostrata la schermata di accesso e dopo aver aggiunto le credenziali posso accedere fluentemente all'api.Autorizzazione da dispositivo Android contro API Spring Rest Rest

Quando provo lo stesso tramite un'APP Android, mi viene sempre riferito alla schermata di accesso. Per eseguire l'autenticazione su Android Side, utilizzo una richiesta API REST che per impostazione predefinita è accessibile. Il browser dell'applicazione Android interno non è compatibile per memorizzare i cookie di sessione? Ogni volta che viene creata una nuova sessione HTTP. Im utilizzando Volley per le richieste

primavera-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> 

il codice nel controller dal API REST

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); 

Primavera di sicurezza DEBUG

************************************************************ 

Request received for POST '/api/user/create': 

[email protected] 

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': 

[email protected] 

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); 

risposta

9

Sospetto che il problema potrebbe essere che si sta implementando il proprio endpoint del controller di autenticazione personalizzato e non si utilizza la catena di sicurezza completa.

La normale catena di filtri verrà comunque richiamata su tutte le richieste come per la configurazione xml, ma semplicemente chiamando il metodo authenticate() in un controller lo farà e non il resto della roba di gestore di autenticazione ad esempio, ad es. in realtà non imposterai un cookie sulla risposta con l'autenticazione del tuo controller ad hoc)

Il modo più semplice per testare questo è semplicemente arricciare l'url direttamente, o usare qualcosa come postman (plugin per chrome apis di resto) per testare il api autenticazione endpoint e verificare se sono state impostate delle cooke nella risposta.

Se si ha il controllo sul codice lato server (ad esempio, si può cambiare e non sono solo lavorando su app Android), qui ci sono alcuni pensieri:

  • Vorrei evitare gli endpoint di autenticazione personalizzati e cercando di far rotolare la roba di sicurezza - la sicurezza di primavera è davvero brava in quella roba.

  • Supponendo di non voler andare con oauth e la complessità di tali soluzioni per l'API, quindi dare un'occhiata a Google's recommended approach for securing APIs for mobile apps. È simile all'approccio che hai preso, ma per l'accesso è sufficiente incorporare una visualizzazione Web nell'app per Android e utilizzarla per consentire all'utente di accedere direttamente al modulo di sicurezza standard di primavera (in modo da sfruttare il comportamento predefinito di Spring ad es. cookie, ecc.), quindi nell'app basta prendere un token utente dalla risposta Web e quindi memorizzarlo, che può quindi essere utilizzato in tutte le richieste API come intestazione della richiesta (in questo modo l'utente non deve continuare ad accedere all'app mobile)

ho scritto una panoramica dell'approccio qui:

Securing your API for mobile access

e abbiamo messo insieme una implementazione di sicurezza primaverile anche qui.

Securing your mobile API with Spring Security

+0

Ho provato il plug postino. Il login funziona in modo fluente con il codice che avevo scritto. quindi la catena di sicurezza sembra essere fatta correttamente. anche quando salti la parte .authenticate sembra funzionare sul lato pc. proverò i tuoi altri suggerimenti. i cookie vengono rimandati – siser

+1

Il login dell'API funziona su Chrome? (ad esempio, disconnetti/cancella i cookie del browser e poi fai una richiesta al resto dell'API passando le credenziali necessarie per quella richiesta API?) Come passi le credenziali di accesso all'autenticazione API? Stai postando un modulo all'API con nome utente/password? – rhinds

+0

ho seguito il tuo suggerimento e l'ho provato con il postmann. Eseguo il logout/svuota la cache del browser, inoltro la richiesta di post e ottenuto una risposta con il cookie. dopo di che posso usare l'API e il backend dell'amministratore. è una richiesta post con username e password che vengono inviati all'api e lì verificati dal codice sopra. nell'esempio con password 123 (che viene internamente codificata). il JSON sembra { "nome utente": "... "" password ":" ..."} e restituisce una stringa come "successo" e un JSESSIONID \t come A13B1D3300B6B7D48ACC4626966872AE come cookie – siser

0

provato il vostro suggerimento e ottenuto che funziona.

Dopo aver controllato, che il back-end trasporta il cookie necessario, aggiusto il volley in modo che memorizzi il cookie trasmesso e lo ritrasmetta su ogni richiesta successivamente come token. Il cookie viene memorizzato nelle preferenze.

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); 
       ..... 
    } 

dichiarata dal vmirinov su go here con modificazione slidly

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 ""; 
    }