3

Spring セキュリティで動作するように JSF ログイン ページを取得しようとしています。私は多くの例を見回しましたが、どれもうまくいきません。JSF ページを使用してログインしようとするたびに、サーバー ログに「資格情報が正しくありません」という警告が表示されます。

Spring-Security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security.xsd">

    <http auto-config="true">
        <intercept-url pattern="/Login.xhtml*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/**/*.css*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/**/*.js*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />
        <form-login login-page="/Login.xhtml" default-target-url="/Secure.xhtml"
            authentication-failure-url="/Login.xhtml" />
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" authorities="ROLE_ADMIN" password="admin"/>
            </user-service>
        </authentication-provider>
    </authentication-manager>
</beans:beans>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.example" />
    <context:annotation-config />
    <tx:annotation-driven />
    <import resource="classpath:spring/security/Spring-Security.xml" />
</beans>

ログイン.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head></h:head>
<body>
    <h:form>
        <h:outputLabel value="username" for="j_username"
            style="float:left" />
        <h:inputText id="j_username" style="float:left" />

        <h:outputLabel value="password" for="j_password"
            style="float:left; clear:both" />
        <h:inputSecret id="j_password" style="float:left" />

        <h:commandButton value="login"
            actionListener="#{loginBean.login}" style="float:left;clear:both" />
    </h:form>
    <h:messages style="float:left;clear:both" />
</body>
</html>

LoginBean

@Named
@Scope("request")
public class LoginBean
{
    public void login() throws ServletException, IOException
    {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        externalContext.dispatch("/j_spring_security_check");
        facesContext.responseComplete();
    }
}

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <filter>
        <filter-name>OpenEntityManagerInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
        <init-param>
            <param-name>singleSession</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>sessionFactoryBeanName</param-name>
            <param-value>sessionFactory</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>OpenEntityManagerInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
</web-app>

非 JSF ページを Login.xhtml として使用すると、問題なく動作します。

機能するページ:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head></h:head>
<body>
    <form action="j_spring_security_check" method="post">
        <table>
            <tr>
                <td>User:</td>
                <td><input type="text" name="j_username" /></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type="password" name="j_password" /></td>
            </tr>
            <tr>
                <td colspan='2'><input name="submit" type="submit"
                    value="submit" /></td>
            </tr>
        </table>
    </form>
</body>
</html>

どんな助けでも大歓迎です。

4

4 に答える 4

3

これは古い問題です。デフォルトでは、FilterSecurityInterceptor はリクエストごとに 1 回だけ実行され、URL に変更がない限りセキュリティの再チェックは行われませんが、JSP/JSF 転送を使用すると、ページは現在のリクエストへの応答としてレンダリングされ、URL はbrowser には、前のページのアドレスが含まれています。

Spring Security 3.0 より前は、次のような GET リクエストを実行することでバイパスされていました。

String encodedURL = externalcontext.encodeResourceURL(externalcontext.getRequestContextPath() + "/j_spring_security_check?j_username=" + username + "&j_password=" + password);

    externalcontext.redirect(encodedURL);

しかし、Spring Security 3.0 から、デフォルトで POST のみをサポートします。

したがって、おそらく最も簡単に使用できる方法の 1 つは、単純な HTML フォームです。それ以外の場合は、AuthenticationManager を取得して手動でリクエストを認証する必要があります。

全体の話は、 Spring フォーラムのこの投稿に端を発していると思います。

そして、最も効果的な例は、ICEFaces wikiにあります。

これは、tutorial.zip からの関連する LoginController クラスです。

/**
 * This class handles all login attempts except html forms that directly
 * post to the /j_spring_security_check method.
 *
 * @author Ben Simpson
 */
@ManagedBean(name = "loginController")
@RequestScoped
public class LoginController implements Serializable {
    private static final long serialVersionUID = 1L;


    /**
     * This action logs the user in and returns to the secure area.
     *
     * @return String path to secure area
     */
    public String loginUsingSpringAuthenticationManager() {
        //get backing bean for simple redirect form
        LoginFormBackingBean loginFormBean =
                (LoginFormBackingBean) FacesUtils.getBackingBean("loginFormBean");
        //authentication manager located in  Spring config: /WEB-INF/authenticationContext-security.xml
        AuthenticationManager authenticationManager =
                (AuthenticationManager) getSpringBean("authenticationManager");
        //simple token holder
        Authentication authenticationRequestToken = createAuthenticationToken(loginFormBean);
        //authentication action
        try {
            Authentication authenticationResponseToken =
                authenticationManager.authenticate(authenticationRequestToken);
            SecurityContextHolder.getContext().setAuthentication(authenticationResponseToken);
            //ok, test if authenticated, if yes reroute
            if (authenticationResponseToken.isAuthenticated()) {
                //lookup authentication success url, or find redirect parameter from login bean
                return "/secure/examples";
            }
        } catch (BadCredentialsException badCredentialsException) {
            FacesMessage facesMessage =
                new FacesMessage("Login Failed: please check your username/password and try again.");
            FacesContext.getCurrentInstance().addMessage(null,facesMessage);
        } catch (LockedException lockedException) {
            FacesMessage facesMessage =
                new FacesMessage("Account Locked: please contact your administrator.");
            FacesContext.getCurrentInstance().addMessage(null,facesMessage);
        } catch (DisabledException disabledException) {
            FacesMessage facesMessage =
                new FacesMessage("Account Disabled: please contact your administrator.");
            FacesContext.getCurrentInstance().addMessage(null,facesMessage);
        }

        return null;
    }

    private Authentication createAuthenticationToken(LoginFormBackingBean loginFormBean) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(
                        loginFormBean.getUserName(),
                        loginFormBean.getPassword()
                );
        return usernamePasswordAuthenticationToken;
    }


    private Object getSpringBean(String name){
        WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(
                (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext());
        return ctx.getBean(name);
    }
}

オプション3:私は個人的に試したことはありませんが、これでもうまくいくはずです:

applicationContext の http 要素で once-per-request 属性を false に設定して、セキュリティの再チェックを強制します。しかし、私はそれをお勧めしません。

<http auto-config="true" use-expressions="true" once-per-request="false">
于 2012-07-31T18:23:39.003 に答える
1

質問への答えは、私を少し欲しがらせました。

したがって、コントローラーの最小限のコードでこれを機能させるために (手動での認証を避けたかったため)、JSF (primefaces) フォームと単純なフォームを組み合わせて使用​​しました。

私はこのようなビューになりました:

<h:form id="login-form" prependId="false">
    <p:focus for="userName" />
    <p:fieldset id="login-fs" legend="User Authentication">
        <h:panelGrid id="login-grid" columns="3">
            <p:outputLabel for="userName" value="User Name" />
            <p:inputText id="userName" value="#{loginView.userName}" required="true" />
            <p:message for="userName" />

            <p:outputLabel for="password" value="Password" />
            <p:inputText type="password" id="password" value="#{loginView.password}" required="true" />
            <p:message for="password" />
        </h:panelGrid>
        <br />
        <p:commandButton value="Submit" icon="ui-icon-check" process="@form" update="login-grid" actionListener="#{loginView.login}" />
    </p:fieldset>
</h:form>

<form id="hidden-form" action="#{request.contextPath}/j_spring_security_check" method="post">
    <h:inputHidden id="j_username" />
    <h:inputHidden id="j_password" />
</form>
<script type="text/javascript">
    function mysubmit() {
        $('#j_username').val($('#userName').val());
        $('#j_password').val($('#password').val());

        $('#hidden-form').submit();
    }
</script>

また、バッキング Bean は典型的な jsf ライフサイクルを実行できます。その後、JavaScript を送り返して、正常に検証された JSF フォームから非表示のフォームに値を転送し、非表示のフォームを送信します。

@ManagedBean
public class LoginView {
    private String userName;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void login() {
        RequestContext.getCurrentInstance().execute("mysubmit()");
    }
}

必要に応じて、送信が実際に行われる前に、サーバー側で必要な他のことを行うことができます。

于 2012-12-19T03:24:30.800 に答える
0

h:commandButtonactionListener の代わりにアクション メソッドを使用するように変更します。

<h:commandButton value="login"
        action="#{loginBean.login}" style="float:left;clear:both" />

参照: action と actionListener の違い

于 2012-07-31T17:13:47.170 に答える
0

私が間違っている場合は誰かが私を修正しますが、バッキング Bean を間違って指定していると思います。

バッキング Bean スコープを指定する JSF の正しい方法は次のようになります。

@ManagedBean
@RequestScoped
public class LoginBean
{
    public void login() throws ServletException, IOException
    {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        externalContext.dispatch("/j_spring_security_check");
        facesContext.responseComplete();
    }
}
于 2012-07-31T15:01:13.813 に答える