web-dev-qa-db-fra.com

sécurité du maillot et gestion des sessions

Existe-t-il un moyen d'obtenir la gestion de session ou la sécurité par programme à Jersey, par exemple gestion de session d'application web? Ou les transactions, les sessions et la sécurité sont-elles toutes gérées par le conteneur dans lequel l'application Jersey est déployée?

42
Adhir

La gestion des sessions est du ressort du conteneur dans lequel Jersey est déployé. Dans la plupart des cas de production, il sera déployé dans un conteneur qui exécute la gestion de session.

Le code ci-dessous est un exemple simple d'une ressource jersey qui obtient l'objet de session et stocke des valeurs dans la session et les récupère lors des appels suivants.

@Path("/helloworld")
public class HelloWorld {

    @GET
    @Produces("text/plain")
    public String hello(@Context HttpServletRequest req) {

        HttpSession session= req.getSession(true);
        Object foo = session.getAttribute("foo");
        if (foo!=null) {
            System.out.println(foo.toString());
        } else {
            foo = "bar";
            session.setAttribute("foo", "bar");
        }
        return foo.toString();


    }
}
70
Jack Cox

Je pensais que les sessions étaient quelque chose que nous devrions jamais utiliser dans les applications RESTful ...

Yegor a raison. Nous ne devons jamais maintenir l'état du côté serveur à la application web conventionnelle. Si vous souhaitez créer une application orientée SOA découplée, vous n'avez pas besoin d'utiliser d'API/d'infrastructure pour les services Web REST. Si vous avez besoin, ou souhaitez, maintenir le client-serveur global du côté serveur, vous construisez implicitement ce que nous pourrions décrire comme une application [Web] orientée SOA, mais en utilisant Jersey comme une sorte de cadre de développement [Web]. Par inadvertance, vous tordez la nature d'un service Web (REST ou autre) Vous pouvez le faire comme il a été suggéré dans la première réponse, mais vous ne devez pas. Le résultat final n'est pas un service Web, juste une application régulière construit avec les outils des services web.

-_o

22
MAM

Oui c'est possible. Jersey documentation dit:

Les informations de sécurité d'une demande sont disponibles en injectant une instance JAX-RS SecurityContext à l'aide de l'annotation @Context. L'instance de contexte de sécurité injectée fournit l'équivalent des fonctionnalités disponibles sur l'API HttpServletRequest. Le contexte de sécurité injecté dépend du déploiement réel de l'application Jersey. Par exemple, pour une application Jersey déployée dans un conteneur Servlet, Jersey SecurityContext encapsulera les informations d'un contexte de sécurité récupéré de la demande Servlet. Dans le cas d'une application Jersey déployée sur un serveur Grizzly, SecurityContext renverra les informations récupérées de la demande Grizzly.

Exemple:

@Path("basket")
public ShoppingBasketResource get(@Context SecurityContext sc) {
    if (sc.isUserInRole("PreferredCustomer") {
        return new PreferredCustomerShoppingBasketResource();
    } else {
        return new ShoppingBasketResource();
    }
}

ou

@Path("resource")
@Singleton
public static class MyResource {
    // Jersey will inject proxy of Security Context
    @Context
    SecurityContext securityContext;

    @GET
    public String getUserPrincipal() {
        return securityContext.getUserPrincipal().getName();
    }
}

Ou si vous voulez que la sécurité soit prête à l'emploi avec des annotations, vérifiez ces documents .

Jersey vous permet également de personnaliser le SecurityContext:

Le SecurityContext peut être récupéré directement à partir de ContainerRequestContext via la méthode getSecurityContext (). Vous pouvez également remplacer le SecurityContext par défaut dans un contexte de demande par un personnalisé à l'aide de la méthode setSecurityContext (SecurityContext). Si vous définissez une instance SecurityContext personnalisée dans votre ContainerRequestFilter, cette instance de contexte de sécurité sera utilisée pour l'injection dans les champs de classe de ressources JAX-RS. De cette façon, vous pouvez implémenter un filtre d'authentification personnalisé qui peut configurer votre propre SecurityContext à utiliser. Pour garantir l'exécution précoce de votre filtre de demande d'authentification personnalisé, définissez la priorité du filtre sur AUTHENTICATION à l'aide des constantes de Priorités. Une exécution précoce de votre filtre d'authentification garantira que tous les autres filtres, ressources, méthodes de ressource et localisateurs de sous-ressources s'exécuteront avec votre instance SecurityContext personnalisée.

Voir exemples d'utilisation des filtres de requête avec Jersey . Et vérifiez mon exemple suivant:

import javax.annotation.Priority;
import javax.ws.rs.Priorities;

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthRequestFilter implements ContainerRequestFilter {
    @Context
    HttpServletRequest webRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        final HttpSession session = webRequest.getSession();

        requestContext.setSecurityContext(new SecurityContext() {
            @Override
            public Principal getUserPrincipal() {
                return new PrincipalImpl((String)session.getAttribute("USER_NAME"));
            }

            @Override
            public boolean isUserInRole(String s) {
                return false;
            }

            @Override
            public boolean isSecure() {
                return false;
            }

            @Override
            public String getAuthenticationScheme() {
                return null;
            }
        });
    }
}

Attention! Ceci a été introduit dans Jersey 2.4 . Glassfish 4.0.0 utilise l'ancien Jersey 2.0, vous devrez donc mettre à niveau Jersey en utilisant ces conseils (il n'est pas prouvé qu'il fonctionne bien). Ou la meilleure façon est de télécharger la version nocturne de Glassfish 4.0.1 . mais ce n'est pas complètement stable pour le moment. J'espère que la nouvelle version sortira bientôt.

MISE À JOUR: Pour le moment (2014-02-14) Glassfish 4.0.1 la version nocturne utilise Jersey 2.5.1 et l'injection de contexte fonctionne très bien.

14

La réponse de Jack au sujet des sessions est correcte. Ils sont spécifiques au conteneur dans lequel vous exécutez, bien que la spécification Servlet vous donne au moins la portabilité entre les conteneurs JavaEE.

En ce qui concerne la sécurité, vous avez au moins la possibilité de le séparer de votre code spécifique JAX-RS en utilisant JaaS (Java Authentication and Authorization Service) et un filtre de servlet . Le filtre peut être utilisé pour appliquer l'authentification HTTP et, en cas d'authentification réussie, configurer le sujet JaaS avec les principaux appropriés. Vos ressources JAX-RS peuvent rechercher les directeurs appropriés sur le sujet. Puisque vous contrôlez l'ensemble de la pile, vous devriez pouvoir compter sur un utilisateur authentifié dans vos ressources (mais testez cela!), Et vous pouvez appliquer l'autorisation en fonction de l'opération en cours dans le code de ressource.

6
StevenC

J'ai résolu ce problème en demandant aux clients d'ajouter l'en-tête d'autorisation et de le tester dans la méthode REST comme ceci:

@GET
@PRODUCES(MediaType.APPLICATION_JSON)
public String returnClients(@Context HTTPServletRequest request(
    String auth = request.getHeader("Authorization");
    Account acc = null;
    if (auth!=null) {
       Account acc = Utils.LoginAccount(auth);
    }
    if (acc == null)
     // not logged in, handle it gracefully

De cette façon, il y a authentification sans démarrer de session.

4
Ron

Pour la sécurité de Jersey, vous devriez jeter un œil sur le maillot OAuth support. OAuth convient parfaitement lorsque vous exposez l'API de votre système à des utilisateurs externes. Par exemple, comme linkedin api

http://wikis.Oracle.com/display/Jersey/OAuth

3
abovesun

Vous pouvez utiliser @path pour regrouper les services sous un espace de nom unique. Exemple .

@Path("/helloworld")
public class HelloWorld {

    @GET
    @Produces("text/plain")
    public String hello() {


        return "";


    }
}
Instead of @Path("/helloworld") use
@Path("admin/helloworld") to expose you class as rest and bind filter on "admin/"
in web.xml as below.

<servlet>
            <servlet-name>jersey-serlvet</servlet-name>
            <servlet-class>com.Sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
            <init-param>
                <param-name>com.Sun.jersey.config.property.packages</param-name>
                <param-value>/</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>jersey-serlvet</servlet-name>
            <url-pattern>/rest/*</url-pattern>
        </servlet-mapping>
         <filter>
            <filter-name>myfilter</filter-name>
            <filter-class>com.Filterclass</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>myfilter</filter-name>
            <url-pattern>/rest/admin/*</url-pattern>
        </filter-mapping> 

    public class Filterclass implements Filter {
       public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain)
                throws IOException, ServletException {
                  try{
                       chain.doFilter(request, response);
                    }catch(Exception e){
                   e.printStackTrace();
                       }
          }
    }

Vous pouvez valider votre session dans cette classe de filtre.

2
ThmHarsh