[jsf] j_security_check를 사용하여 Java EE / JSF에서 사용자 인증 수행

JSF 2.0 (및 구성 요소가있는 경우)을 사용하는 웹 응용 프로그램의 사용자 인증과 JPA의 사용자 정보가있는 Java EE 6 핵심 메커니즘 (로그인 / 권한 확인 / 로그 아웃)에 대한 현재 접근 방식이 무엇인지 궁금합니다. 실재. Oracle Java EE 튜토리얼은 이것에 대해 조금 부족합니다 (서블릿 만 처리).

이것은 Spring-Security (acegi) 또는 Seam과 같은 다른 프레임 워크를 사용 하지 않지만 가능한 경우 새로운 Java EE 6 플랫폼 (웹 프로파일)을 고수하려고합니다.



답변

웹을 검색하고 다양한 방법을 시도한 후 Java EE 6 인증에 대해 제안하는 내용은 다음과 같습니다.

보안 영역을 설정하십시오.

제 경우에는 데이터베이스에 사용자가있었습니다. 따라서이 블로그 게시물을 따라 데이터베이스 테이블의 사용자 이름 및 MD5 해시 암호를 기반으로 사용자를 인증 할 수있는 JDBC 영역을 만들었습니다.

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

참고 :이 게시물은 데이터베이스의 사용자 및 그룹 테이블에 대해 설명합니다. javax.persistence 주석을 통해 데이터베이스에 매핑 된 UserType enum 속성을 가진 User 클래스가 있습니다. userType 열을 그룹 열로 사용하여 사용자 및 그룹에 대해 동일한 테이블로 영역을 구성했으며 정상적으로 작동했습니다.

양식 인증 사용 :

여전히 위의 블로그 게시물을 따라 web.xml 및 sun-web.xml을 구성하지만 BASIC 인증을 사용하는 대신 FORM을 사용하십시오 (실제로 어떤 것을 사용하든 상관 없지만 FORM을 사용했습니다). JSF가 아닌 표준 HTML을 사용하십시오.

그런 다음 위의 BalusC 팁을 사용하여 데이터베이스에서 사용자 정보를 초기화하십시오. 그는 얼굴 컨텍스트에서 프린시 펄을 가져 오는 관리 Bean에서이를 수행하도록 제안했습니다. 대신 각 사용자에 대한 세션 정보를 저장하기 위해 상태 저장 세션 빈을 사용했기 때문에 세션 컨텍스트를 주입했습니다.

 @Resource
 private SessionContext sessionContext;

주체로, 사용자 이름을 확인하고 EJB Entity Manager를 사용하여 데이터베이스에서 사용자 정보를 가져 와서 SessionInformationEJB에 저장할 수 있습니다.

로그 아웃:

또한 로그 아웃하는 가장 좋은 방법을 찾아 보았습니다. 내가 찾은 가장 좋은 것은 서블릿을 사용하는 것입니다.

 @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());
  }
 }

질문 날짜를 고려하면 답변이 늦었지만 Google에서 온 다른 사람들에게 도움이 되었기를 바랍니다.

챠오,

비 토르 수자


답변

나는 당신이 원하는 가정 기반 인증을 구성 하여 배포 설명j_security_check.

당신은 또한 그냥 같은 개의 정의 된 필드 이름을 사용하여 JSF에서이 작업을 수행 할 수 있습니다 j_usernamej_password튜토리얼에서 설명한다.

예 :

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

당신은 게으른 로딩을 할 수있는 User(가) 있는지 확인하는 게터 User이미 그렇지 않은 경우 로그온이 있다면, 확인 Principal요청에 존재 그렇다면, 그 취득 User과 관련 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;
    }

}

User의해 JSF EL에서 분명히 액세스 할 수 있습니다 #{auth.user}.

로그 아웃하려면 HttpServletRequest#logout()(및 Usernull로 설정하십시오 ). HttpServletRequest로 JSF에서 핸들을 얻을 수 있습니다 ExternalContext#getRequest(). 세션을 완전히 무효화 할 수도 있습니다.

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

나머지 (배포 설명자 및 영역에서 사용자, 역할 및 제약 조건 정의)의 경우 Java EE 6 학습서 및 서블릿 컨테이너 문서를 일반적인 방식으로 따르십시오.


업데이트 : 일부 서블릿 컨테이너의 디스패처가 도달 할 수없는 새로운 Servlet 3.0 HttpServletRequest#login()을 사용하여 프로그래밍 방식으로 로그인 j_security_check할 수도 있습니다. 이 경우 당신은 fullworthy JSF의 형태와 콩 사용할 수 있습니다 usernamepassword속성과 login같이 방법 :

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

그리고이 뷰의 범위는 관리 Bean으로 처음 요청 된 페이지를 기억합니다.

@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.
}

이 방법은 User에 의해 JSF EL에서 액세스 할 수 있습니다 #{user}.


답변

인증 문제를 전면 컨트롤러 (예 : Apache 웹 서버)에 완전히 남겨두고 대신 HttpServletRequest.getRemoteUser ()를 평가하는 옵션 인 REMOTE_USER 환경 변수의 JAVA 표현입니다. 또한 Shibboleth 인증과 같은 정교한 로그인 디자인이 가능합니다. 웹 서버를 통해 서블릿 컨테이너에 대한 요청 필터링은 프로덕션 환경에 적합한 디자인이며 종종 mod_jk가 사용됩니다.


답변

HttpServletRequest.login이 세션에서 인증 상태를 설정하지 않는 문제 3.0.1에서 수정되었습니다. glassfish를 최신 버전으로 업데이트하면 완료됩니다.

업데이트는 매우 간단합니다.

glassfishv3/bin/pkg set-authority -P dev.glassfish.org
glassfishv3/bin/pkg image-update


답변