0

这个问题是对这里的跟进。

登录后,我的应用程序需要使用 request.login() 查询容器进行身份验证,然后设置一个“用户”bean,其中包含用户名、密码、角色和特定于位置的字段。在对容器(WAS 8)成功授权后,需要检查用户的角色以找出应将用户重定向到的适当欢迎页面。我的容器实现了一个联合存储库,它基本上将 3 个 LDAP 分支、一个数据库和一个平面文件集中在一起。有很多角色(现在5个,以后可能会更多)。

我有 2 个 bean:一个 RequestScoped Credentials bean 和一个名为 Login 的 SessionScoped bean。登录包含一个 login() 方法。我在登录方法方面有问题。

我的问题是,每当我使用:

Principal userPrincipal = request.getUserPrincipal();
request.getUserPrincipal();
if (userPrincipal != null) {
    request.logout();
}
request.login(credentials.getUsername(), credentials.getPassword());
String name = userPrincipal.getName();

前:

Donor donor = loginService.getDonor(credentials.getUsername());
currentUser = new Users();
currentUser.setLocation(donor.getCenter().getCity());
currentUser.setRole("DONOR");
currentUser.setUserId(credentials.getUsername());
currentUser.setFirstName(donor.getFirstName());
currentUser.setLastName(donor.getLastName());
currentUser.setUsername(credentials.getUsername());
currentUser.setName(credentials.getUsername());
return "users?faces-redirect=true";

我的 currentUser 用户 bean 没有存储在会话中。

我删除了第一块代码,并注意到我的 User bean 的信息随后存储在会话中,并且可以在后续用户页面上查看。我逐行重新引入了第一块代码,并注意到:

if (userPrincipal != null) {
    request.logout();
}

导致问题。

我到底应该如何存储实现编程安全性和 JSF2.0/CDI 的用户 bean?我已经为此花费了数周时间。我可以想象实现一些更精细的东西,比如捕获重定向的过滤器,抓取用户主体,调用 db 或 ldap 以获取其他属性,然后重定向到适当的页面......但我想让事情保持简单。必须有一个简单的方法来做到这一点。

我最初尝试使用 j_security_check 登录并在 web.xml 中指定 FORM。现在,使用 JSF 2.0 表单和登录方法,我注意到 FORM 被 BASIC 忽略了。System.out.println("getAuthType?.." + request.getAuthType()); 在登录方法中返回“BASIC”。这会导致烦人的缓存,这就是需要 request.logout 的原因。否则,request.login 将失败。

我昨晚偶然发现了这个,其中包含指向此处的链接。因此,可能有一种方法可以获取 userprincipal、设置 User bean 并重定向到适当的欢迎页面。我不知道第二个链接有多过时。j_security_check 似乎也不能与确定我应该根据角色将用户重定向到哪个页面的编程方式结合使用。

至于我应该使用 j_security_check 还是编程安全/JSF/CDI,我只需要弄清楚一些简单的东西,并允许我在会话中存储 Login bean 或独立的 User bean。

从我的角度来看,这是我的登录表单:

<h:form id="loginForm">
    <fieldset>
        <div class="form-row">
            <h:outputLabel for="username" value="User ID"/>
            <h:inputText id="username" value="#{credentials.username}" 
required="true" size="20" />
        </div>
        <div class="form-row">
            <h:outputLabel for="password" value="Password"/>
            <h:inputSecret id="password" type="password" value="#
{credentials.password}" required="true" />
        </div>

        <div class="form-row"> 
            <h:commandButton styleClass="btn btn-warning" value="Sign In" 
type="submit" action="#{login.login}" />
            <a href="#" id="forgot-password">Forgot you password?</a>
        </div>     
    </fieldset>
</h:form>

这是我的登录 bean(代码已被删除和编辑以展示相关部分):

import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.security.auth.Subject;
import javax.security.auth.login.CredentialExpiredException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
@SessionScoped
@Named
public class Login implements Serializable {

    private static final long serialVersionUID = 7965455427888195913L;

    @Inject
    private Credentials credentials;

    @PersistenceContext
    private EntityManager userDatabase;

    @Inject
    LoginService loginService;

    private Users currentUser;
    private Service service;
    private String uniqueSecurityName;
    private String l;

    @SuppressWarnings("unchecked")
    public String login() {
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext externalContext = context.getExternalContext();
        HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
        System.out.println("The login method has been called.");

        try {
        Principal userPrincipal = request.getUserPrincipal();
        request.getUserPrincipal();
        if (userPrincipal != null) {
            request.logout();
        }
        request.login(credentials.getUsername(), credentials.getPassword());
        String name = userPrincipal.getName();

        System.out.println("getRemoteUser?.." + request.getRemoteUser());
        System.out.println("getUserPrincipal?.." + request.getUserPrincipal());
        System.out.println("getAuthType?.." + request.getAuthType());         

        Donor donor = loginService.getDonor(credentials.getUsername());
        currentUser = new Users();
        currentUser.setLocation(donor.getCenter().getCity());
        currentUser.setRole("DONOR");
        currentUser.setUserId(credentials.getUsername());
        currentUser.setFirstName(donor.getFirstName());
        currentUser.setLastName(donor.getLastName());
        currentUser.setUsername(credentials.getUsername());
        currentUser.setName(credentials.getUsername());
        return "users?faces-redirect=true";
        } catch (Exception e) {}

        return null;
    }
    public void logout() {
        currentUser = null;
    }

    public boolean isLoggedIn() {
        return currentUser != null;
    }

    @Produces
    @LoggedIn
    public Users getCurrentUser() {
        return currentUser;
    }
}

这是我的凭据 bean:

import java.io.Serializable;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Default;
import javax.inject.Named;

@RequestScoped
@Named
@Default
public class Credentials implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 6976596855571123825L;
    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;
    }
}

这是随后的 users.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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
      <title>Login</title>
      <link href="style/main.css" rel="stylesheet" type="text/css"/>
      <ui:insert name="head"/>
    </head>
<body>

  <div id="container">
    <div id="header">

    </div>

    <div id="sidebar">

    </div>

    <div id="content">
      <h1>Current User</h1>
        <h:dataTable value="#{login.currentUser}" var="u">
            <h:column>
                <f:facet name="header">
                 Username
              </f:facet>
                <h:outputText value="#{u.username}" />
            </h:column>
            <h:column>
                <f:facet name="header">
                 Name
              </f:facet>
                <h:outputText value="#{u.name}" />
            </h:column>
            <h:column>
                <f:facet name="header">
                 Password
              </f:facet>
                <h:outputText value="#{u.password}" />
            </h:column>
        </h:dataTable>
    </div>

    <br style="clear:both"/>
  </div>

</body>
</html>

这是我的用户 bean:

import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;

@Entity
public class Users {
    @Id
    private String username;
    private String name;
    private String password;
    @Transient
    private String role;
    @Transient
    private String location;
    @Transient
    private String userId;
    @Transient
    private String firstName;
    @Transient
    private String lastName;

    public Users() {
    }

    public String getUsername() {
        return username;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "User (username = " + username + ", name = " + name + ")";
    }
}
4

1 回答 1

1

你的问题和你猜的一样

 if (userPrincipal != null) {
            request.logout();
        }

您正在使用上述行重新创建会话。在会话范围的 bean 中销毁会话不是一个好主意。这导致会话 bean 被 CDI 清理。

您需要在操作方法中清理会话 bean 时存储用户名、密码。

@ApplicationScoped
class LoginService{

public boolean login(String username,String password) {
                String username = credentials.getUsername();
                String password = credentials.getPassword();
                Principal userPrincipal = request.getUserPrincipal();
                request.getUserPrincipal();
                if (userPrincipal != null) {
                    request.logout();
                }
                boolean loggedIn = request.login(username,password);
                if(loggedIn){
                users = new Users();
                users.setUsername(username);  
                session.setAttribute("Users",users);               
                }
              return loggedIn;
    }

}

保留登录凭据的操作方法

  @RequestScoped  
    class Credential {
          @Inject  LoginService loginService;
          String username;
          String password;

          login(ActionEvent action){
           if(loginService.login(username,password))
               //redirect to users
            else
               //show error
          }
    }

创建注释以从 CDI 获取登录用户

@Retention(RetentionPolicy.RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface SpringBean {
    String value() default "";
}

使用 BeanLocator 类来生成一些对象,您可以将此方法添加到 LoginService 但最好将其分开

    public class BeanLocator {    
    @Produces @LoggedInUser
    public Object getSessionAttribute(InjectionPoint ip){
        String beanName = ip.getAnnotated().
                              getAnnotation(SessionAttribute.class).value();
        if(beanName==null || beanName.equals(""))
            beanName = getDefaultBeanName(ip.getMember().
                               getDeclaringClass().getName());
        return  FacesContext.getCurrentInstance().getExternalContext().
                    getSessionMap().get(beanName);
    }
   } 

在其他服务中使用它,例如,

class MyOtherBean{
 @Inject @@LoggedInUser Users;
}
于 2013-03-25T17:34:40.743 回答