Authentification RESTful via Spring

Problème:
Nous avons une API RESTful basée sur Spring MVC qui contient des informations sensibles. L’API doit être sécurisée, mais l’envoi des informations d’identification de l’utilisateur (combo utilisateur / pass) à chaque demande n’est pas souhaitable. Conformément aux directives REST (et aux exigences métier internes), le serveur doit restr sans état. L’API sera utilisée par un autre serveur dans une approche de type mashup.

Exigences:

  • Le client fait une demande à .../authenticate (URL non protégée) avec des informations d’identification; Le serveur renvoie un jeton sécurisé contenant suffisamment d’informations pour que le serveur valide les demandes futures et rest sans état. Ce serait probablement composé des mêmes informations que le jeton Remember-Me de Spring Security.

  • Le client effectue les requêtes suivantes sur diverses URL (protégées), en ajoutant le jeton précédemment obtenu en tant que paramètre de requête (ou, de manière moins souhaitable, un en-tête de requête HTTP).

  • Le client ne peut pas s’attendre à stocker des cookies.

  • Comme nous utilisons déjà Spring, la solution devrait utiliser Spring Security.

Nous nous sums cogné la tête contre le mur en essayant de faire fonctionner ce système, alors j’espère que quelqu’un a déjà résolu ce problème.

Étant donné le scénario ci-dessus, comment pourriez-vous résoudre ce besoin particulier?

Nous avons réussi à faire en sorte que cela fonctionne exactement comme décrit dans l’OP, et j’espère que quelqu’un d’autre pourra utiliser la solution. Voici ce que nous avons fait:

Configurez le contexte de sécurité comme suit:

          

Comme vous pouvez le voir, nous avons créé un AuthenticationEntryPoint personnalisé, qui retourne simplement un 401 Unauthorized si la requête n’a pas été authentifiée dans la chaîne de filtrage par notre AuthenticationTokenProcessingFilter .

CustomAuthenticationEntryPoint :

 public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." ); } } 

AuthenticationTokenProcessingFilter :

 public class AuthenticationTokenProcessingFilter extends GenericFilterBean { @Autowired UserService userService; @Autowired TokenUtils tokenUtils; AuthenticationManager authManager; public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) { this.authManager = authManager; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @SuppressWarnings("unchecked") Map parms = request.getParameterMap(); if(parms.containsKey("token")) { Ssortingng token = parms.get("token")[0]; // grab the first "token" parameter // validate the token if (tokenUtils.validate(token)) { // determine the user based on the (already validated) token UserDetails userDetails = tokenUtils.getUserFromToken(token); // build an Authentication object with the user's info UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request)); // set the authentication into the SecurityContext SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(authentication)); } } // continue thru the filter chain chain.doFilter(request, response); } } 

De toute évidence, TokenUtils contient un TokenUtils privé (et très spécifique à chaque cas) et ne peut pas être facilement partagé. Voici son interface:

 public interface TokenUtils { Ssortingng getToken(UserDetails userDetails); Ssortingng getToken(UserDetails userDetails, Long expiration); boolean validate(Ssortingng token); UserDetails getUserFromToken(Ssortingng token); } 

Cela devrait vous permettre de bien commencer. Heureux codage 🙂

Vous pouvez envisager l’ authentification Digest Access . Le protocole est essentiellement le suivant:

  1. La demande est faite à partir du client
  2. Le serveur répond avec une chaîne de nonce unique
  3. Le client fournit un nom d’utilisateur et un mot de passe (et d’autres valeurs) md5 haché avec le nonce; ce hachage est appelé HA1
  4. Le serveur est alors en mesure de vérifier l’identité du client et de servir les documents demandés.
  5. La communication avec le nonce peut continuer jusqu’à ce que le serveur fournisse un nouveau nonce (un compteur est utilisé pour éliminer les attaques par rejeu)

Toutes ces communications se font via des en-têtes, ce qui, comme le souligne jmort253, est généralement plus sécurisé que la communication de données sensibles dans les parameters url.

L’authentification Digest Access est prise en charge par Spring Security . Notez que, même si les documents indiquent que vous devez avoir access au mot de passe en texte brut de votre client, vous pouvez vous authentifier avec succès si vous avez le hachage HA1 pour votre client.

En ce qui concerne les jetons contenant des informations, les jetons Web JSON ( http://jwt.io ) sont une technologie shinye. Le concept principal consiste à incorporer des éléments d’information (revendications) dans le jeton, puis à signer l’intégralité du jeton afin que la fin de la validation puisse vérifier que les revendications sont effectivement fiables.

J’utilise cette implémentation Java: https://bitbucket.org/b_c/jose4j/wiki/Home

Il y a aussi un module Spring (spring-security-jwt), mais je n’ai pas examiné ce qu’il supporte.

Pourquoi ne commencez-vous pas à utiliser OAuth avec JSON WebTokens?

http://projects.spring.io/spring-security-oauth/

OAuth2 est un protocole / cadre d’autorisation standardisé. Selon les spécifications officielles OAuth2:

Vous pouvez trouver plus d’informations ici