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


156

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

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

답변:


85

웹을 검색하고 다양한 방법을 시도한 후 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에서 온 다른 사람들에게 도움이 되었기를 바랍니다.

챠오,

비 토르 수자


15
작은 조언 : request.getSession (false)을 사용하고 invalidate ()를 호출합니다. 세션이 없으면 request.getSession (false)가 null을 반환 할 수 있습니다. 더 나은 체크하면 처음의 널 (null))
Arjan Tijms

@Vitor : Hi .. 컨테이너 기반 보안에서 시로 또는 기타와 같은 대안으로 전환하는 것이 좋은 시점에 대해 이야기하고 싶습니까? 여기에 더 초점을 맞춘 질문을 참조 : stackoverflow.com/questions/7782720/...
라자 굽타

Glassfish JDBC Realm은 솔트 된 비밀번호 해시 저장을 지원하지 않는 것 같습니다. 이 경우에 사용하는 것이 가장 좋은 방법입니까?
Lii

죄송합니다. 도와 드릴 수 없습니다. 저는 Glassfish 전문가가 아닙니다. 사람들이 무엇을 말하는지 알기 위해 새 스레드에서 그 질문을 할 수 있습니까?
Vítor E. Silva Souza

1
Lii, 당신은 glassfish 컨테이너를 사용하여 소금으로 작업 할 수 있습니다. 해시를 사용하지 않도록 Healm을 구성하십시오. 비밀번호에 삽입 한 일반 값을 비교하여 HttpServletResponse#login(user, password)사용자의 소금, 반복 및 소금에 사용하는 모든 것을 DB에서 얻을 수 있으며 사용자가 소금을 사용하여 입력 한 비밀번호를 해시 한 다음 컨테이너에 인증을 요청합니다 HttpServletResponse#login(user, password).
emportella

152

나는 당신이 원하는 가정 기반 인증을 구성 하여 배포 설명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}.


1
디스패처를 포함하여 j_security_check일부 서블릿 컨테이너에서 작동하지 않을 수 있는 질문을 업데이트했습니다 .
BalusC

1
: 웹 응용 프로그램과 프로그램 적 보안 사용에 자바 튜토리얼에서 링크 java.sun.com/javaee/6/docs/tutorial/doc/gjiie.html를 사용할 수있는 서블릿 클래스 : (서블릿 사용) @WebServlet(name="testServlet", urlPatterns={"/ testServlet "}) @ServletSecurity(@HttpConstraint(rolesAllowed = {"testUser", "admin”})) 그리고 당 방법에 레벨 : @ServletSecurity(httpMethodConstraints={ @HttpMethodConstraint("GET"), @HttpMethodConstraint(value="POST", rolesAllowed={"testUser"})})
ngeek

3
그리고 요점은 ..? 이것이 JSF에 해당되는지 여부 글쎄, JSF에는 하나의 서블릿 만 있으며 FacesServlet,이를 수정할 수없고 원하지도 않는다.
BalusC

1
@BalusC-위의 방법이 j_security_check 또는 프로그램 로그인을 사용하는 것이 가장 좋은 방법이라고 말할 때?
simgineer

3
@simgineer : 요청한 URL은에 의해 정의 된 이름을 가진 요청 속성으로 사용 가능합니다 RequestDispatcher.FORWARD_REQUEST_URI. 요청 속성은에서 제공하는 JSF ExternalContext#getRequestMap()입니다.
BalusC

7

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


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.