【问题标题】:JSF 2.0 + Programmatic authentication = 403 :)JSF 2.0 + 程序化身份验证 = 403 :)
【发布时间】:2026-01-01 08:20:04
【问题描述】:

我正在过渡到 GlassFish 3.1.2,但似乎无法解决身份验证问题。

议程很简单:我想要一个登录 bean 为用户进行编程身份验证。身份验证似乎有效(代码通过 login() 方法),但服务器最终显示受保护资源的 403...请帮助 :)

这里有更多细节。有一个带有名称/密码对的纯 JSF 登录页面:

<ui:composition 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"
    xmlns:p="http://primefaces.org/ui" template="/templates/main.xhtml">

    <ui:define name="body">
        <h:form id="form">
            <p:messages />

            <p:panel>
                <h:panelGrid>
                    <h:outputText value="User Name" />
                    <p:inputText value="#{loginBean.userName}" id="userName"
                        required="true" />
                    <p:message for="userName" />

                    <h:outputText value="Password" />
                    <p:password value="#{loginBean.password}" id="password"
                        required="true" />
                    <p:message for="password" />

                </h:panelGrid>

                <h:panelGrid columns="2">
                    <p:commandButton value="Clear"
                        actionListener="#{loginBean.clear()}" ajax="false" />

                    <p:commandButton value="Login" action="#{loginBean.login()}"
                        ajax="false" />
                </h:panelGrid>
            </p:panel>
        </h:form>
    </ui:define>
</ui:composition>

和一个执行登录的bean

@Named("loginBean")
@SessionScoped
public class LoginBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    @Named("dao")
    private Dao dao;

    private String userName;

    private String password;

    @Inject
    private UserBean userBean;

    ...

    public String login() {
        HttpServletRequest request = (HttpServletRequest) FacesContext
                .getCurrentInstance().getExternalContext().getRequest();

        try {
            request.login(getUserName(), getPassword());

            Principal principal = request.getUserPrincipal();

            logger.info("Logged in successfully: " + principal);
        } catch (ServletException e) {
            Messages.addError("Invalid user name or password.");
            return null;
        }

        User user = dao.findSingle("SELECT u FROM User AS u WHERE u.name = ?1",
                getUserName());

        if (user == null) {
            logger.severe("Unable to find user record after successful authentication");

            Messages.addError("Unable to load user record");
            try {
                request.logout();
            } catch (ServletException e) {
                logger.log(Level.SEVERE, "Unable to logout after failed login attempt", e);
            }
            return null;
        }

        getUserBean().setUser(user);

        return "/list/list.xhtml?faces-redirect=true";
    }

    ... 

    accessors

    ...
}

这是我的 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">

    <display-name>GM</display-name>

    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>

    <!-- Faces Servlet -->
    <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>

    <login-config>
        <realm-name>gmRealm</realm-name>
    </login-config>

    <security-role>
        <role-name>user</role-name>
    </security-role>

    <security-role>
        <role-name>admin</role-name>
    </security-role>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected Area</web-resource-name>
            <url-pattern>/list/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admin Area</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <welcome-file-list>
        <welcome-file>login.xhtml</welcome-file>
    </welcome-file-list>

</web-app>

这是项目的文件夹结构:

我还能够按照此处所述设置领域:http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html

没什么花哨的,AFAICT。

但是,每当我在成功登录后访问受保护的资源时,我都会得到:

HTTP Status 403 - Access to the requested resource has been denied

尽管服务器日志包含消息:

INFO: Logged in successfully: nick

还请注意,当我从 login() 方法的返回值中删除“?faces-redirect = true”时,初始受保护资源在登录后立即呈现,但所有后续请求都失败并显示 403 .

这是它在调试器中显示的内容:

我也认为我已经完成了我的功课:

Performing user authentication in Java EE / JSF using j_security_check

Programmatically control login with Servlet 3.0

JSF 2.0 Simple login page

Glassfish 3 security - Form based authentication using a JDBC Realm

请帮忙...

【问题讨论】:

  • 您是否尝试过返回“list/list.xhtml?faces-redirect=true”(不带“/”)或“list.xhtml?faces-redirect=true”(不带“/list” )?另外,请显示您的文件夹结构,看看导航规则是否有问题。
  • 感谢您的建议。两者都试过了,list/list.xhtml 给出了相同的 403,而 list.xhtml 给出了“无法找到与 from-view-id '/login.xhtml' 匹配的导航案例,用于操作 '#{loginBean.login()}'结果为 'list.xhtml?faces-redirect=true'"
  • 你的约束配置表明角色admin的用户无权访问list目录。这是你想要的吗?如果不是,您应该将角色 admin 添加到保护区约束。
  • 感谢您指出这一点,马特。管理员是真正的数据管理员,不一定需要列表功能。我担心对于具有正确角色映射的经过身份验证的用户,我会“拒绝访问”。我最担心的是我找不到问题所在:)

标签: jsf java-ee-6 glassfish-3 servlet-3.0


【解决方案1】:

哦,好吧,我想这个太针对部署了。我最终在 Spring 中为 Tomcat 堆栈重新设计了原型。现在像魅力一样工作:)。

【讨论】:

    【解决方案2】:

    我认为您在 Spring Security 中不允许使用 jsf 资源。在 http 标记的 security.xml 中添加以下行。

    <http .........>
        <intercept-url pattern="/javax.faces.resource/**" access="permitAll"/>
    </http>
    

    【讨论】: