2011-02-06 30 views
12

Ho implementato questo processo di sicurezza nel mio progetto: Spring Security 3 - MVC Integration Tutorial (Part 2).Spring Security Accesso Ajax

Il mio problema è che ho bisogno di trasformarlo in un login basato su Ajax. Cosa devo fare per rendere questo XML adatto con la restituzione di stringa/JSON al client? Capisco che il problema potrebbe essere nel tag form-login.

<?xml version="1.0" encoding="UTF-8"?> 
<beans:beans xmlns="http://www.springframework.org/schema/security" 
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
          http://www.springframework.org/schema/security 
          http://www.springframework.org/schema/security/spring-security-3.0.xsd"> 

    <!-- This is where we configure Spring-Security --> 
    <http auto-config="true" use-expressions="true" access-denied-page="/Management/auth/denied" > 

     <intercept-url pattern="/Management/auth/login" access="permitAll"/> 
     <intercept-url pattern="/Management/main/admin" access="hasRole('ROLE_ADMIN')"/> 
     <intercept-url pattern="/Management/main/common" access="hasRole('ROLE_USER')"/> 

     <form-login 
       login-page="/Management/auth/login" 
       authentication-failure-url="/Management/auth/login?error=true" 
       default-target-url="/Management/main/common"/> 

     <logout 
       invalidate-session="true" 
       logout-success-url="/Management/auth/login" 
       logout-url="/Management/auth/logout"/> 

    </http> 

    <!-- Declare an authentication-manager to use a custom userDetailsService --> 
    <authentication-manager> 
      <authentication-provider user-service-ref="customUserDetailsService"> 
        <password-encoder ref="passwordEncoder"/> 
      </authentication-provider> 
    </authentication-manager> 

    <!-- Use a Md5 encoder since the user's passwords are stored as Md5 in the database --> 
    <beans:bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/> 

    <!-- A custom service where Spring will retrieve users and their corresponding access levels --> 
    <beans:bean id="customUserDetailsService" class="com.affiliates.service.CustomUserDetailsService"/> 

</beans:beans> 

risposta

19

Questo è un vecchio post, ma si presenta ancora come uno dei migliori risultati per "accesso di sicurezza ajax di sicurezza", quindi ho pensato di condividere la mia soluzione. Segue gli standard Spring Security ed è abbastanza semplice da configurare, il trucco è di avere 2 elementi <http> nella configurazione di sicurezza, uno per REST/Ajax e uno per il resto dell'app (pagine HTML normali). L'ordine di visualizzazione di <http> è importante, deve passare da URL più specifici a URL più generici, proprio come gli elementi <url-intercept> all'interno di uno <http>.

Passo 1: Impostazione Due <http> separati l'

<?xml version="1.0" encoding="UTF-8"?> 
<beans:beans xmlns="http://www.springframework.org/schema/security" 
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> 

    <!-- a shared request cache is required for multiple http elements --> 
    <beans:bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache" /> 

    <!-- remove security from static resources to avoid going through the security filter chain --> 
    <http pattern="/resources/**" security="none" /> 

    <!-- http config for REST services (AJAX interface) 
    =================================================== --> 
    <http auto-config="true" use-expressions="true" pattern="/rest/**"> 
     <!-- login configuration 
      login-processing-url="/rest/security/login-processing" front-end AJAX requests for authentication POST to this URL 
      login-page="/rest/security/login-page" means "authentication is required" 
      authentication-failure-url="/rest/security/authentication-failure" means "authentication failed, bad credentials or other security exception" 
      default-target-url="/rest/security/default-target" front-end AJAX requests are redirected here after success authentication 
     --> 
     <form-login 
      login-processing-url="/rest/security/login-processing" 
      login-page="/rest/security/login-page" 
      authentication-failure-url="/rest/security/authentication-failure" 
      default-target-url="/rest/security/default-target" 
      always-use-default-target="true" /> 
     <logout logout-url="/rest/security/logout-url" /> 

     <!-- REST services can be secured here, will respond with JSON instead of HTML --> 
     <intercept-url pattern="/rest/calendar/**" access="hasRole('ROLE_USER')" /> 

     <!-- other REST intercept-urls go here --> 

     <!-- end it with a catch all --> 
     <intercept-url pattern="/rest/**" access="isAuthenticated()" /> 

     <!-- reference to the shared request cache --> 
     <request-cache ref="requestCache"/> 
    </http> 

    <!-- http config for regular HTML pages 
    =================================================== --> 
    <http auto-config="true" use-expressions="true"> 
     <form-login 
      login-processing-url="/security/j_spring_security_check" 
      login-page="/login" 
      authentication-failure-url="/login?login_error=t" /> 
     <logout logout-url="/security/j_spring_security_logout" /> 

     <intercept-url pattern="/calendar/**" access="hasRole('ROLE_USER')" /> 

     <!-- other intercept-urls go here --> 

     <!-- in my app's case, the HTML config ends with permitting all users and requiring HTTPS 
      it is always a good idea to send sensitive information like passwords over HTTPS --> 
     <intercept-url pattern="/**" access="permitAll" requires-channel="https" /> 

     <!-- reference to the shared request cache --> 
     <request-cache ref="requestCache"/> 
    </http> 

    <!-- authentication manager and other configuration go below --> 
</beans:beans> 

Fase 2: REST controller di autenticazione

import org.springframework.http.HttpHeaders; 
import org.springframework.http.HttpStatus; 
import org.springframework.http.ResponseEntity; 
import org.springframework.security.core.Authentication; 
import org.springframework.security.core.context.SecurityContextHolder; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 

import flexjson.JSONSerializer; 

@Controller 
@RequestMapping(value = "/rest/security") 
public class RestAuthenticationController { 

    public HttpHeaders getJsonHeaders() { 
     HttpHeaders headers = new HttpHeaders(); 
     headers.add("Content-Type", "application/json"); 
     return headers; 
    } 

    @RequestMapping(value="/login-page", method = RequestMethod.GET) 
    public ResponseEntity<String> apiLoginPage() { 
     return new ResponseEntity<String>(getJsonHeaders(), HttpStatus.UNAUTHORIZED); 
    } 

    @RequestMapping(value="/authentication-failure", method = RequestMethod.GET) 
    public ResponseEntity<String> apiAuthenticationFailure() { 
     // return HttpStatus.OK to let your front-end know the request completed (no 401, it will cause you to go back to login again, loops, not good) 
     // include some message code to indicate unsuccessful login 
     return new ResponseEntity<String>("{\"success\" : false, \"message\" : \"authentication-failure\"}", getJsonHeaders(), HttpStatus.OK); 
    } 

    @RequestMapping(value="/default-target", method = RequestMethod.GET) 
    public ResponseEntity<String> apiDefaultTarget() { 
     Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
     // exclude/include whatever fields you need 
     String userJson = new JSONSerializer().exclude("*.class", "*.password").serialize(authentication); 
     return new ResponseEntity<String>(userJson, getJsonHeaders(), HttpStatus.OK); 
    } 
} 

Fase 3: Invia modulo AJAX ed elaborare la risposta, necessaria libreria ajaxForm di jQuery

<form action="/rest/security/login-processing" method="POST"> 
... 
</form> 

$('form').ajaxForm({ 
    success: function(response, statusText, xhr, $form) { 
     console.log(response); 
     if(response == null || response.username == null) { 
      alert("authentication failure"); 
     } else { 
      // response is JSON version of the Spring's Authentication 
      alert("authentication success"); 
     } 
    }, 
    error: function(response, statusText, error, $form) { 
     if(response != null && response.message == "authentication-failure") { 
      alert("authentication failure"); 
     } 
    } 
}); 
+0

La soluzione sembra bello, ma dove è il metodo di controllo rispondendo a '/ resto/security/login-processing" definito –

+0

@MichaelDeKeyser -? È mappato UsernamePasswordAuthenticationFilter fornito da SpringSecurity, dare un'occhiata ai documenti e di ricerca per "UsernamePasswordAuthenticationFilter" e "login-processing-url" http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/reference/htmlsingle/ – SergeyB

+1

Ho provato a farlo tramite JavaConfig di Spring La sicurezza, alla fine, ha dovuto rinunciare e usare il ftw XML! – Boon

2

La primavera si sta spostando dalle configurazioni basate su XML e verso le classi Java @Configuration. Di seguito è riportata la versione @Configuration della configurazione spiegata nel post in alto (Spring Security Ajax login). Passaggi 2 & 3 rimangono gli stessi, sostituire il punto 1 con questo codice. L'ordine è ancora una volta importante, le definizioni più specifiche devono essere caricate prima di quelle più generiche, usare @Order(1) e @Order(2) per controllarlo.

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.annotation.Order; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.builders.WebSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.web.savedrequest.HttpSessionRequestCache; 
import org.springframework.security.web.savedrequest.RequestCache; 

@Configuration 
@EnableWebSecurity 
public class WebMvcSecurityConfiguration extends WebSecurityConfigurerAdapter { 

    @Bean(name = "requestCache") 
    public RequestCache getRequestCache() { 
     return new HttpSessionRequestCache(); 
    } 

    @Configuration 
    @Order(1) 
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { 

      @Autowired private RequestCache requestCache; 

      @Override 
      protected void configure(HttpSecurity http) throws Exception { 
       http 
        .regexMatcher("/rest.*") 
        .authorizeRequests() 
         .antMatchers("/rest/calendar/**") 
          .hasAuthority("ROLE_USER") 
         .antMatchers("/rest/**") 
          .permitAll() 
         .and() 
        .headers() 
         .xssProtection() 
         .and() 
        .logout() 
         .logoutUrl("/rest/security/logout-url") 
         .and() 
        .requestCache() 
         .requestCache(requestCache) 
         .and() 
        .formLogin() 
         .loginProcessingUrl("/rest/security/login-processing") 
         .loginPage("/rest/security/login-page") 
         .failureUrl("/rest/security/authentication-failure") 
         .defaultSuccessUrl("/rest/security/default-target", false) 
         .and() 
        .httpBasic(); 
      } 
    } 

    @Configuration 
    @Order(2) 
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { 

     @Autowired private RequestCache requestCache; 

     @Override 
     protected void configure(HttpSecurity http) throws Exception { 
      http 
       .authorizeRequests() 
        .regexMatchers("/calendar/.*") 
         .hasAuthority("ROLE_USER") 
        .regexMatchers("/.*") 
         .permitAll() 
        .and() 
       .logout() 
        .logoutUrl("/security/j_spring_security_logout") 
        .and() 
       .requestCache() 
        .requestCache(requestCache) 
        .and() 
       .formLogin() 
        .loginProcessingUrl("/security/j_spring_security_check") 
        .loginPage("/login") 
        .failureUrl("/login?login_error=t") 
        .and() 
       .httpBasic(); 
     } 
    } 

    @Override 
    public void configure(WebSecurity web) throws Exception { 
     web 
      .ignoring() 
       .antMatchers("/resources/**") 
       .antMatchers("/sitemap.xml"); 
    } 
} 
Problemi correlati