Java EE / JSF'de j_security_check kullanarak kullanıcı kimlik doğrulaması yapma


156

JSF 2.0 (ve herhangi bir bileşen varsa) ve Java EE 6 çekirdek mekanizmaları (oturum açma / kontrol izinleri / çıkışları) kullanan bir web uygulaması için kullanıcı kimlik doğrulaması hakkında bir JPA kullanıcı tutma ile ilgili geçerli yaklaşım merak ediyorum varlıktır. Oracle Java EE öğretici bu konuda biraz seyrek (sadece sunucu uygulamaları işler).

Bu olmadan İlkbahar-Güvenlik (Acegi) veya Dikiş gibi bütün diğer çerçeve, yararlanarak, fakat mümkünse yeni bir Java EE 6 platformu (web profiline) ile umarım uymaya çalışıyorum.

Yanıtlar:


85

Web'de arama yaptıktan ve birçok farklı yol denedikten sonra, Java EE 6 kimlik doğrulaması için önereceğim şey:

Güvenlik alanını kurun:

Benim durumumda, veritabanında kullanıcılar vardı. Bu nedenle, blog tablondaki kullanıcı adı ve MD5 karma parolalarına göre kullanıcıların kimliğini doğrulayabilecek bir JDBC Alanı oluşturmak için bu blog gönderisini izledim:

http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html

Not: Gönderi, veritabanındaki bir kullanıcı ve grup tablosundan bahseder. Ben javax.persistence ek açıklamaları veritabanına eşlenen bir UserType enum özniteliği olan bir User sınıfı vardı. Alanı, grup sütunu olarak userType sütununu kullanarak kullanıcılar ve gruplar için aynı tabloyla yapılandırdım ve iyi çalıştı.

Form kimlik doğrulamasını kullan:

Yine de yukarıdaki blog gönderisini takip ederek, web.xml ve sun-web.xml dosyanızı yapılandırın, ancak BASIC kimlik doğrulaması kullanmak yerine FORM'u kullanın (aslında hangisini kullandığınız önemli değil, ancak FORM'u kullandım). JSF'yi değil, standart HTML'yi kullanın.

Daha sonra yukarıdaki BalusC'nin ipucunu kullanıcı bilgilerinin veritabanından başlatılmasında tembel olarak kullanın. Anaparayı yüzler bağlamından alarak yönetilen bir fasülyede yapmayı önerdi. Bunun yerine, her kullanıcı için oturum bilgilerini saklamak için durum bilgisi olan bir oturum fasulyesi kullandım, bu yüzden oturum bağlamını enjekte ettim:

 @Resource
 private SessionContext sessionContext;

Müdür ile, kullanıcı adını kontrol edebilir ve EJB Entity Manager'ı kullanarak Kullanıcı bilgilerini veritabanından alabilir ve SessionInformationEJB'imde saklayabilirim .

Çıkış Yap:

Ayrıca çıkış yapmanın en iyi yolunu aradım. Bulduğum en iyi şey bir Servlet kullanmak:

 @WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"})
 public class LogoutServlet extends HttpServlet {
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   HttpSession session = request.getSession(false);

   // Destroys the session for this user.
   if (session != null)
        session.invalidate();

   // Redirects back to the initial page.
   response.sendRedirect(request.getContextPath());
  }
 }

Cevabım sorunun tarihi göz önüne alındığında gerçekten geç olsa da, umarım bu da tıpkı benim yaptığım gibi Google'dan gelen diğer insanlara yardımcı olur.

Ciao,

Vítor Souza


15
Küçük bir öneri: request.getSession (false) kullanıyorsunuz ve bu konuda invalidate () çağırıyorsunuz. Hiçbir oturum yoksa request.getSession (false) null değerini döndürebilir. Önce boş olup olmadığını kontrol edin;)
Arjan Tijms

@Vitor: Merhaba ... Konteyner tabanlı güvenlikten shiro veya diğerleri gibi alternatiflere geçmenin ne zaman iyi olduğu hakkında bir şey söylemek ister misiniz? Burada daha fazla odaklanmış soru görün: stackoverflow.com/questions/7782720/…
Rajat Gupta

Görünüşe göre Glassfish JDBC Alanı tuzlanmış parola karmaları depolamayı desteklemiyor. Bu durumda kullanmak gerçekten en iyi uygulama mı?
Lii

Üzgünüm, sana yardım edemem. Ben Glassfish uzmanı değilim. Belki bu soruyu yeni bir başlıkta insanların ne dediğini görmelerini isteyin?
Vítor E. Silva Souza

1
Lii, cambalığı kabını kullanarak tuzlu çalışabilirsiniz. Sağlığınızı herhangi bir karma kullanmayacak şekilde yapılandırın. HttpServletResponse#login(user, password)Parola için girdiğiniz düz değeri karşılaştırır, böylece kullanıcının tuzundan, yinelemelerden ve tuzlama için kullandığınız her şeyden DB alabilir, kullanıcının bu tuzu kullanarak girdiği parolayı hash edip kapsayıcıdan kimlik doğrulaması yapmasını isteyebilirsiniz. HttpServletResponse#login(user, password).
emportella

152

Dağıtım tanımlayıcıları ve kullanarak form tabanlı kimlik doğrulaması istediğinizi varsayalım .j_security_check

Ayrıca, sadece aynı predefinied alan adlarını kullanarak JSF yapabilirsiniz j_usernameve j_passwordöğretici gösterildiği gibi.

Örneğin

<form action="j_security_check" method="post">
    <h:outputLabel for="j_username" value="Username" />
    <h:inputText id="j_username" />
    <br />
    <h:outputLabel for="j_password" value="Password" />
    <h:inputSecret id="j_password" />
    <br />
    <h:commandButton value="Login" />
</form>

UserGirişte olup olmadığını kontrol etmek için alıcıda tembel yükleme yapabilirsiniz Userve eğer değilse, Principalistekte mevcut olup olmadığını kontrol edin ve eğer öyleyse, Userilişkili olanı alın j_username.

package com.stackoverflow.q2206911;

import java.io.IOException;
import java.security.Principal;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

@ManagedBean
@SessionScoped
public class Auth {

    private User user; // The JPA entity.

    @EJB
    private UserService userService;

    public User getUser() {
        if (user == null) {
            Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
            if (principal != null) {
                user = userService.find(principal.getName()); // Find User by j_username.
            }
        }
        return user;
    }

}

UserTarafından MTU EL belli ki erişilebilir #{auth.user}.

Çıkış yapmak için bir HttpServletRequest#logout()(ve Usernull olarak ayarlayın !). HttpServletRequestTarafından JSF bir tanıtıcı alabilirsiniz ExternalContext#getRequest(). Ayrıca oturumu tamamen geçersiz kılabilirsiniz.

public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    return "login?faces-redirect=true";
}

Kalan kısım için (dağıtım tanımlayıcı ve alandaki kullanıcıları, rolleri ve kısıtlamaları tanımlamak için) Java EE 6 öğreticisini ve servletcontainer belgelerini her zamanki gibi uygulayın.


Güncelleme : Yeni Servlet 3.0'ı , bazı sunucu uygulamaları kapsayıcılarında bir dağıtıcı tarafından erişilemeyen HttpServletRequest#login()programlı bir oturum açmak için de kullanabilirsiniz j_security_check. Bu durumda, fullworthy bir JSF formu ve usernameve passwordözelliklerine sahip bir fasulye ve aşağıdaki gibi bir loginyöntem kullanabilirsiniz:

<h:form>
    <h:outputLabel for="username" value="Username" />
    <h:inputText id="username" value="#{auth.username}" required="true" />
    <h:message for="username" />
    <br />
    <h:outputLabel for="password" value="Password" />
    <h:inputSecret id="password" value="#{auth.password}" required="true" />
    <h:message for="password" />
    <br />
    <h:commandButton value="Login" action="#{auth.login}" />
    <h:messages globalOnly="true" />
</h:form>

Ve bu görüş, başlangıçta talep edilen sayfayı da hatırlayan yönetilen fasulyeyi kapsamıştır:

@ManagedBean
@ViewScoped
public class Auth {

    private String username;
    private String password;
    private String originalURL;

    @PostConstruct
    public void init() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);

        if (originalURL == null) {
            originalURL = externalContext.getRequestContextPath() + "/home.xhtml";
        } else {
            String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);

            if (originalQuery != null) {
                originalURL += "?" + originalQuery;
            }
        }
    }

    @EJB
    private UserService userService;

    public void login() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext externalContext = context.getExternalContext();
        HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();

        try {
            request.login(username, password);
            User user = userService.find(username, password);
            externalContext.getSessionMap().put("user", user);
            externalContext.redirect(originalURL);
        } catch (ServletException e) {
            // Handle unknown username/password in request.login().
            context.addMessage(null, new FacesMessage("Unknown login"));
        }
    }

    public void logout() throws IOException {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        externalContext.invalidateSession();
        externalContext.redirect(externalContext.getRequestContextPath() + "/login.xhtml");
    }

    // Getters/setters for username and password.
}

Bu yolla UserJSF EL'de tarafından erişilebilir #{user}.


1
Soruyu, gönderimin j_security_checktüm servletcontainers'ta çalışmayabileceği bir feragatname içerecek şekilde güncelledim .
BalusC

1
Web Uygulamalarıyla Programatik Güvenlik Kullanma Java Öğreticisinden bağlantı: java.sun.com/javaee/6/docs/tutorial/doc/gjiie.html (Servlet kullanarak): Bir sunucu uygulaması sınıfında şunları kullanabilirsiniz: @WebServlet(name="testServlet", urlPatterns={"/ testServlet "}) @ServletSecurity(@HttpConstraint(rolesAllowed = {"testUser", "admin”})) Ve bir yöntem başına seviye: @ServletSecurity(httpMethodConstraints={ @HttpMethodConstraint("GET"), @HttpMethodConstraint(value="POST", rolesAllowed={"testUser"})})
ngeek

3
Ve ne demek istiyorsun ..? Bunun JSF'de geçerli olup olmadığı? JSF'de sadece bir sunucu uygulaması var FacesServletve onu değiştiremezsiniz (ve değiştirmek istemezsiniz).
BalusC

1
@BalusC - Yukarıdakilerin en iyi yol olduğunu söylediğinizde, j_security_check veya programlı bir giriş kullanmayı mı kastediyorsunuz?
simgineer

3
@simgineer: İstenen URL, adının tanımladığı gibi bir istek özelliği olarak kullanılabilir RequestDispatcher.FORWARD_REQUEST_URI. İstek öznitelikleri JSF tarafından mevcuttur ExternalContext#getRequestMap().
BalusC

7

Kimlik doğrulama sorunlarını ön denetleyiciye, örneğin bir Apache Web sunucusuna tamamen bırakmanın ve bunun yerine REMOTE_USER ortam değişkeni için JAVA temsili olan HttpServletRequest.getRemoteUser () yönteminin değerlendirilmesinin bir seçenek olduğu belirtilmelidir. Bu ayrıca Shibboleth kimlik doğrulaması gibi tasarımlarda sofistike oturum açmaya izin verir. Filtreleme Bir web sunucusu üzerinden sunucu uygulaması kapsayıcısına yönelik istekleri, üretim ortamları için iyi bir tasarımdır, genellikle mod_jk bunu yapmak için kullanılır.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.