Java EE 安全性 - 登录后未重定向到初始页面 [英] Java EE security - Not redirected to initial page after login

查看:75
本文介绍了Java EE 安全性 - 登录后未重定向到初始页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚开始学习 Java EE.我的目标是为羽毛球运动员实现一个门户网站(使用 EJB 3 和 JSF),用户可以在其中发布和分析他们的结果.

I just started learning Java EE. My goal is to implement a web portal (with EJB 3 and JSF) for badminton players, where the users can post and analyze their results.

为了简单起见(事实证明事实并非如此)我决定使用容器提供的安全系统(JBoss as7).在遇到一些问题后,我设法使身份验证/授权工作.但是,我有一个我无法解决的问题.

To keep it simple (well it turns out it really isn't) I have decided to use the security system the container provides (JBoss as7). After some problem I have managed to get the authentication/authorization to work. However, I have one issue which I haven't been able to solve.

当我尝试访问受保护的页面时,正如预期的那样,被安全系统拦截.但是,在我登录后,我并没有被重定向到我最初请求的页面.相反,我再次被要求登录.如果我手动输入原始地址,我可以毫无问题地访问该页面.

When I try to access a protected page I get, as expected, intercepted by the security system. However, after I have logged in I'm not re-directed to the page I initially requested. Instead I'm once more asked to log in. If I manually type the original address, I can access the page without any trouble.

我已经阅读了很多关于 stackoverflow 的帖子,但都无法解决我的问题.如果有人能帮助我,我将不胜感激!

I have read many threads on stackoverflow, but haven't been able to solve my problem. I would really appreciate if someone could help me!

Authentication.java:

@ManagedBean
@SessionScoped
public class Authentication {

    private String username = "";
    private String password = "";

    private User user = new User();

    @EJB
    UserService service;

    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 User getUser() {
        return user;
    }

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

        try {
            Principal userPrincipal = request.getUserPrincipal();
            if (userPrincipal != null) {
                request.logout();
            }
            request.login(username, password);
            user = service.find(username, password);
        } catch (ServletException e) {
            context.addMessage(null, new FacesMessage("Unknown login"));
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext()
            .invalidateSession();
        return "login";
    }
}

login.xhtml

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

home.xhtml

<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'
xmlns:f='http://java.sun.com/jsf/core'
xmlns:h='http://java.sun.com/jsf/html'
xmlns:ui='http://java.sun.com/jsf/facelets'
xmlns:p="http://primefaces.org/ui">
<h:head>
    <link type="text/css" rel="stylesheet"
        href="#{request.contextPath}/themes/cupertino/skin.css" />
</h:head>
<h:body>
    <h:form>
        <h:commandButton action="login" value="Log in" />
    </h:form>
</h:body>

web.xml

....
<display-name>BadmintonPortal</display-name>
<welcome-file-list>
    <welcome-file>/pages/protected/user/user_home.xhtml</welcome-file>
</welcome-file-list>
<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>/faces/*</url-pattern>
    <url-pattern>*.jsf</url-pattern>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
    <param-name>primefaces.THEME</param-name>
    <param-value>cupertino</param-value>
</context-param>

<!-- Protected area definition -->
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Restricted Area - ADMIN Only</web-resource-name>
        <url-pattern>/pages/protected/admin/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Restricted Area - USER and ADMIN</web-resource-name>
        <url-pattern>/pages/protected/user/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>user</role-name>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

<!-- Login page -->
<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/pages/public/login.xhtml</form-login-page>
        <form-error-page>/pages/public/loginError.xhtml</form-error-page>
    </form-login-config>
</login-config>

<!-- System roles -->
<security-role>
    <role-name>admin</role-name>
</security-role>
<security-role>
    <role-name>user</role-name>
</security-role>

忘记包含faces-config.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<faces-config 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-facesconfig_2_1.xsd"
    version="2.1">

    <navigation-rule>
        <navigation-case>
            <from-outcome>login</from-outcome>
            <to-view-id>/pages/protected/user/user_home.xhtml</to-view-id>  
            <redirect/>
        </navigation-case>
    </navigation-rule>
</faces-config>

jboss-web.xml

<?xml version='1.0' encoding='UTF-8'?>

<jboss-web>
    <context-root>BadmintonPortal</context-root>
    <security-domain>java:/jaas/BadmintonPortalRealm</security-domain>
</jboss-web>

编辑 2:

可行的解决方案

@ManagedBean
@ViewScoped
public class Authentication {
    ...
    public Authentication() {
        ExternalContext eContext = FacesContext.getCurrentInstance()
            .getExternalContext();
        uri = (String) eContext.getRequestMap().get(
            RequestDispatcher.FORWARD_REQUEST_URI);
    }

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

        try {
            Principal userPrincipal = request.getUserPrincipal();
            if (userPrincipal != null) {
                request.logout();
            }
            request.login(username, password);
            user = service.find(username, password);
            context.getExternalContext().getSessionMap().put("user", user);
            context.getExternalContext().redirect(uri);
        } catch (ServletException e) {
            context.addMessage(null, new FacesMessage("Unknown login"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext()
            .invalidateSession();
        return "login";
    }
}

推荐答案

您将通过使用 JSF 表单接管登录部分,该表单在提交时在 JSF 托管 bean 中执行程序化登录.如果您使用的是直接提交到容器管理的身份验证 URL j_security_check 的纯 HTML 表单,如这个答案,那么你确实会被自动重定向到初始页面.

You're taking over the login part by using a JSF form which performs a programmatic login in a JSF managed bean upon submit. If you were using a plain HTML form which submits directly to the container managed authentication URL j_security_check as outlined in the first part of this answer, then you would indeed automatically be redirected to the initial page.

但是,由于您自己使用 JSF 进行登录,那么您也应该自己使用 JSF 导航到初始页面.由于登录页面通常由服务器端转发通过 RequestDispatcher#forward(),最初请求的页面可作为请求属性使用RequestDispatcher.FORWARD_REQUEST_URI 常数.在 JSF 术语中,这因此可用如下:

However, since you're taking the login in own hands using JSF yourself, then you should also be navigating to the initial page using JSF yourself. As the login page is usually opened by a server-side forward by RequestDispatcher#forward(), the initially requested page is available as a request attribute with the name as specified in RequestDispatcher.FORWARD_REQUEST_URI constant. In JSF terms, that's thus available as follows:

String originalURI = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);

(请记住,在返回 null 的情况下有一个后备 URL,即当它被直接打开而没有首先点击受限 URL 时)

(keep in mind to have a fallback URL for the case it returns null, i.e. when it's been opened directly without hitting a restricted URL first)

收集它的最佳位置是与登录页面相关联的 @ViewScoped bean 的(post)构造函数.使用当前的会话作用域 bean 方法,最好将其设置为视图作用域,并在登录期间将 User 显式放入会话作用域,如下所示:

The best place to collect it would be the (post)constructor of a @ViewScoped bean associated with the login page. With the current session scoped bean approach, it'd probably be better to make it a view scoped one and during login explicitly put the User in the session scope as follows:

externalContext.getSessionMap().put("user", user);

这样用户就可以通过 #{user} 直接访问,而不是 #{authentication.user}.

this way the user is available by #{user} directly instead of #{authentication.user}.

这篇关于Java EE 安全性 - 登录后未重定向到初始页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆